206 changed files with 2668 additions and 1144 deletions
@ -0,0 +1 @@ |
|||||||
|
rsync yudao-server/target/yudao-server.jar root@39.105.46.0:/root/project/zen/yudao.jar |
@ -0,0 +1,41 @@ |
|||||||
|
package cn.iocoder.yudao.config; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import me.chanjar.weixin.cp.api.WxCpService; |
||||||
|
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; |
||||||
|
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; |
||||||
|
import org.springframework.beans.factory.annotation.Value; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.context.annotation.Scope; |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@Slf4j |
||||||
|
public class WxCpConfigure { |
||||||
|
|
||||||
|
@Value("${corpId}") |
||||||
|
private String corpId; |
||||||
|
private String farmAppid; |
||||||
|
private String farmSecret; |
||||||
|
|
||||||
|
@Bean |
||||||
|
@Scope("singleton") |
||||||
|
public WxCpService wxCpService(){ |
||||||
|
|
||||||
|
log.info("333:{}",corpId); |
||||||
|
WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl(); |
||||||
|
|
||||||
|
config.setCorpId("wwb9f9734e8e124761"); |
||||||
|
config.setAgentId(1000033); |
||||||
|
config.setCorpSecret("UDSKsn0_LAPYqSwjH9E-AfY_X40lq0sormfe1yV_6Gc"); |
||||||
|
|
||||||
|
WxCpServiceImpl wxCpService = new WxCpServiceImpl(); |
||||||
|
wxCpService.setWxCpConfigStorage(config); |
||||||
|
|
||||||
|
log.info("企业微信初始化:{}",wxCpService); |
||||||
|
|
||||||
|
return wxCpService; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
||||||
|
cn.iocoder.yudao.config.WxCpConfigure |
@ -1,4 +1,4 @@ |
|||||||
package co.yixiang.domain; |
package cn.iocoder.yudao.framework.mybatis.core.dataobject; |
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.FieldFill; |
import com.baomidou.mybatisplus.annotation.FieldFill; |
||||||
import com.baomidou.mybatisplus.annotation.TableField; |
import com.baomidou.mybatisplus.annotation.TableField; |
@ -1,137 +1,137 @@ |
|||||||
package cn.iocoder.yudao.module.member.service.user; |
//package cn.iocoder.yudao.module.member.service.user;
|
||||||
|
//
|
||||||
import cn.hutool.core.util.RandomUtil; |
//import cn.hutool.core.util.RandomUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
//import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; |
//import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
//import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; |
//import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||||
import cn.iocoder.yudao.module.infra.api.file.FileApi; |
//import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||||
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; |
//import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
|
||||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
//import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
//import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||||
import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl; |
//import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl;
|
||||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
//import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||||
import org.junit.jupiter.api.Test; |
//import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean; |
//import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import; |
//import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate; |
//import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder; |
//import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
//
|
||||||
import javax.annotation.Resource; |
//import javax.annotation.Resource;
|
||||||
import java.io.ByteArrayInputStream; |
//import java.io.ByteArrayInputStream;
|
||||||
import java.util.function.Consumer; |
//import java.util.function.Consumer;
|
||||||
|
//
|
||||||
import static cn.hutool.core.util.RandomUtil.*; |
//import static cn.hutool.core.util.RandomUtil.*;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
//import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; |
//import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.eq; |
//import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.when; |
//import static org.mockito.Mockito.when;
|
||||||
|
//
|
||||||
// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
//// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
||||||
/** |
///**
|
||||||
* {@link MemberUserServiceImpl} 的单元测试类 |
// * {@link MemberUserServiceImpl} 的单元测试类
|
||||||
* |
// *
|
||||||
* @author 宋天 |
// * @author 宋天
|
||||||
*/ |
// */
|
||||||
@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) |
//@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class})
|
||||||
public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest { |
//public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||||
|
//
|
||||||
@Resource |
// @Resource
|
||||||
private MemberUserServiceImpl memberUserService; |
// private MemberUserServiceImpl memberUserService;
|
||||||
|
//
|
||||||
@Resource |
// @Resource
|
||||||
private StringRedisTemplate stringRedisTemplate; |
// private StringRedisTemplate stringRedisTemplate;
|
||||||
|
//
|
||||||
@Resource |
// @Resource
|
||||||
private MemberUserMapper userMapper; |
// private MemberUserMapper userMapper;
|
||||||
|
//
|
||||||
@MockBean |
// @MockBean
|
||||||
private MemberAuthServiceImpl authService; |
// private MemberAuthServiceImpl authService;
|
||||||
|
//
|
||||||
@MockBean |
// @MockBean
|
||||||
private PasswordEncoder passwordEncoder; |
// private PasswordEncoder passwordEncoder;
|
||||||
|
//
|
||||||
@MockBean |
// @MockBean
|
||||||
private SmsCodeApi smsCodeApi; |
// private SmsCodeApi smsCodeApi;
|
||||||
@MockBean |
// @MockBean
|
||||||
private FileApi fileApi; |
// private FileApi fileApi;
|
||||||
|
//
|
||||||
@Test |
// @Test
|
||||||
public void testUpdateNickName_success(){ |
// public void testUpdateNickName_success(){
|
||||||
// mock 数据
|
// // mock 数据
|
||||||
MemberUserDO userDO = randomUserDO(); |
// MemberUserDO userDO = randomUserDO();
|
||||||
userMapper.insert(userDO); |
// userMapper.insert(userDO);
|
||||||
|
//
|
||||||
// 随机昵称
|
// // 随机昵称
|
||||||
String newNickName = randomString(); |
// String newNickName = randomString();
|
||||||
|
//
|
||||||
// 调用接口修改昵称
|
// // 调用接口修改昵称
|
||||||
memberUserService.updateUserNickname(userDO.getId(),newNickName); |
// memberUserService.updateUserNickname(userDO.getId(),newNickName);
|
||||||
// 查询新修改后的昵称
|
// // 查询新修改后的昵称
|
||||||
String nickname = memberUserService.getUser(userDO.getId()).getNickname(); |
// String nickname = memberUserService.getUser(userDO.getId()).getNickname();
|
||||||
// 断言
|
// // 断言
|
||||||
assertEquals(newNickName,nickname); |
// assertEquals(newNickName,nickname);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
@Test |
// @Test
|
||||||
public void testUpdateAvatar_success() throws Exception { |
// public void testUpdateAvatar_success() throws Exception {
|
||||||
// mock 数据
|
// // mock 数据
|
||||||
MemberUserDO dbUser = randomUserDO(); |
// MemberUserDO dbUser = randomUserDO();
|
||||||
userMapper.insert(dbUser); |
// userMapper.insert(dbUser);
|
||||||
|
//
|
||||||
// 准备参数
|
// // 准备参数
|
||||||
Long userId = dbUser.getId(); |
// Long userId = dbUser.getId();
|
||||||
byte[] avatarFileBytes = randomBytes(10); |
// byte[] avatarFileBytes = randomBytes(10);
|
||||||
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); |
// ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
|
||||||
// mock 方法
|
// // mock 方法
|
||||||
String avatar = randomString(); |
// String avatar = randomString();
|
||||||
when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar); |
// when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar);
|
||||||
// 调用
|
// // 调用
|
||||||
String str = memberUserService.updateUserAvatar(userId, avatarFile); |
// String str = memberUserService.updateUserAvatar(userId, avatarFile);
|
||||||
// 断言
|
// // 断言
|
||||||
assertEquals(avatar, str); |
// assertEquals(avatar, str);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
@Test |
// @Test
|
||||||
public void updateMobile_success(){ |
// public void updateMobile_success(){
|
||||||
// mock数据
|
// // mock数据
|
||||||
String oldMobile = randomNumbers(11); |
// String oldMobile = randomNumbers(11);
|
||||||
MemberUserDO userDO = randomUserDO(); |
// MemberUserDO userDO = randomUserDO();
|
||||||
userDO.setMobile(oldMobile); |
// userDO.setMobile(oldMobile);
|
||||||
userMapper.insert(userDO); |
// userMapper.insert(userDO);
|
||||||
|
//
|
||||||
// TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
// // TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
||||||
// 旧手机和旧验证码
|
// // 旧手机和旧验证码
|
||||||
// SmsCodeDO codeDO = new SmsCodeDO();
|
//// SmsCodeDO codeDO = new SmsCodeDO();
|
||||||
String oldCode = RandomUtil.randomString(4); |
// String oldCode = RandomUtil.randomString(4);
|
||||||
// codeDO.setMobile(userDO.getMobile());
|
//// codeDO.setMobile(userDO.getMobile());
|
||||||
// codeDO.setCode(oldCode);
|
//// codeDO.setCode(oldCode);
|
||||||
// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
//// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
||||||
// codeDO.setUsed(Boolean.FALSE);
|
//// codeDO.setUsed(Boolean.FALSE);
|
||||||
// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
//// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
||||||
|
//
|
||||||
// 更新手机号
|
// // 更新手机号
|
||||||
String newMobile = randomNumbers(11); |
// String newMobile = randomNumbers(11);
|
||||||
String newCode = randomNumbers(4); |
// String newCode = randomNumbers(4);
|
||||||
AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO(); |
// AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO();
|
||||||
reqVO.setMobile(newMobile); |
// reqVO.setMobile(newMobile);
|
||||||
reqVO.setCode(newCode); |
// reqVO.setCode(newCode);
|
||||||
reqVO.setOldMobile(oldMobile); |
// reqVO.setOldMobile(oldMobile);
|
||||||
reqVO.setOldCode(oldCode); |
// reqVO.setOldCode(oldCode);
|
||||||
memberUserService.updateUserMobile(userDO.getId(),reqVO); |
// memberUserService.updateUserMobile(userDO.getId(),reqVO);
|
||||||
|
//
|
||||||
assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile); |
// assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
// ========== 随机对象 ==========
|
// // ========== 随机对象 ==========
|
||||||
|
//
|
||||||
@SafeVarargs |
// @SafeVarargs
|
||||||
private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) { |
// private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) {
|
||||||
Consumer<MemberUserDO> consumer = (o) -> { |
// Consumer<MemberUserDO> consumer = (o) -> {
|
||||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
// o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||||
}; |
// };
|
||||||
return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers)); |
// return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers));
|
||||||
} |
// }
|
||||||
|
//
|
||||||
} |
//}
|
||||||
|
@ -0,0 +1,100 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser; |
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
import javax.annotation.Resource; |
||||||
|
import org.springframework.validation.annotation.Validated; |
||||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
|
||||||
|
import javax.validation.constraints.*; |
||||||
|
import javax.validation.*; |
||||||
|
import javax.servlet.http.*; |
||||||
|
import java.util.*; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
import cn.iocoder.yudao.module.system.convert.CpUser.CpUserConvert; |
||||||
|
import cn.iocoder.yudao.module.system.service.CpUser.CpUserService; |
||||||
|
|
||||||
|
@Api(tags = "管理后台 - 企业微信成员") |
||||||
|
@RestController |
||||||
|
@RequestMapping("/system/cp-user") |
||||||
|
@Validated |
||||||
|
public class CpUserController { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private CpUserService cpUserService; |
||||||
|
|
||||||
|
@PostMapping("/create") |
||||||
|
@ApiOperation("创建企业微信成员") |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:create')") |
||||||
|
public CommonResult<Long> createCpUser(@Valid @RequestBody CpUserCreateReqVO createReqVO) { |
||||||
|
return success(cpUserService.createCpUser(createReqVO)); |
||||||
|
} |
||||||
|
|
||||||
|
@PutMapping("/update") |
||||||
|
@ApiOperation("更新企业微信成员") |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:update')") |
||||||
|
public CommonResult<Boolean> updateCpUser(@Valid @RequestBody CpUserUpdateReqVO updateReqVO) { |
||||||
|
cpUserService.updateCpUser(updateReqVO); |
||||||
|
return success(true); |
||||||
|
} |
||||||
|
|
||||||
|
@DeleteMapping("/delete") |
||||||
|
@ApiOperation("删除企业微信成员") |
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:delete')") |
||||||
|
public CommonResult<Boolean> deleteCpUser(@RequestParam("id") Long id) { |
||||||
|
cpUserService.deleteCpUser(id); |
||||||
|
return success(true); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/get") |
||||||
|
@ApiOperation("获得企业微信成员") |
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:query')") |
||||||
|
public CommonResult<CpUserRespVO> getCpUser(@RequestParam("id") Long id) { |
||||||
|
CpUserDO cpUser = cpUserService.getCpUser(id); |
||||||
|
return success(CpUserConvert.INSTANCE.convert(cpUser)); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/list") |
||||||
|
@ApiOperation("获得企业微信成员列表") |
||||||
|
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:query')") |
||||||
|
public CommonResult<List<CpUserRespVO>> getCpUserList(@RequestParam("ids") Collection<Long> ids) { |
||||||
|
List<CpUserDO> list = cpUserService.getCpUserList(ids); |
||||||
|
return success(CpUserConvert.INSTANCE.convertList(list)); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/page") |
||||||
|
@ApiOperation("获得企业微信成员分页") |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:query')") |
||||||
|
public CommonResult<PageResult<CpUserRespVO>> getCpUserPage(@Valid CpUserPageReqVO pageVO) { |
||||||
|
PageResult<CpUserDO> pageResult = cpUserService.getCpUserPage(pageVO); |
||||||
|
return success(CpUserConvert.INSTANCE.convertPage(pageResult)); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/export-excel") |
||||||
|
@ApiOperation("导出企业微信成员 Excel") |
||||||
|
@PreAuthorize("@ss.hasPermission('system:cp-user:export')") |
||||||
|
@OperateLog(type = EXPORT) |
||||||
|
public void exportCpUserExcel(@Valid CpUserExportReqVO exportReqVO, |
||||||
|
HttpServletResponse response) throws IOException { |
||||||
|
List<CpUserDO> list = cpUserService.getCpUserList(exportReqVO); |
||||||
|
// 导出 Excel
|
||||||
|
List<CpUserExcelVO> datas = CpUserConvert.INSTANCE.convertList02(list); |
||||||
|
ExcelUtils.write(response, "企业微信成员.xls", "数据", CpUserExcelVO.class, datas); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
import javax.validation.constraints.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Base VO,提供给添加、修改、详细的子 VO 使用 |
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class CpUserBaseVO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户昵称", required = true) |
||||||
|
@NotNull(message = "用户昵称不能为空") |
||||||
|
private String userid; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "头像", required = true) |
||||||
|
@NotNull(message = "头像不能为空") |
||||||
|
private String avatar; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "状态", required = true) |
||||||
|
@NotNull(message = "状态不能为空") |
||||||
|
private Integer status; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "手机号", required = true) |
||||||
|
@NotNull(message = "手机号不能为空") |
||||||
|
private String mobile; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "姓名") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "部门") |
||||||
|
private Object department; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "职位") |
||||||
|
private String position; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "邮件") |
||||||
|
private String email; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "企业邮件") |
||||||
|
private String bizMail; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
import javax.validation.constraints.*; |
||||||
|
|
||||||
|
@ApiModel("管理后台 - 企业微信成员创建 Request VO") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ToString(callSuper = true) |
||||||
|
public class CpUserCreateReqVO extends CpUserBaseVO { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Excel VO |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class CpUserExcelVO { |
||||||
|
|
||||||
|
@ExcelProperty("编号") |
||||||
|
private Long id; |
||||||
|
|
||||||
|
@ExcelProperty("用户昵称") |
||||||
|
private String userid; |
||||||
|
|
||||||
|
@ExcelProperty("头像") |
||||||
|
private String avatar; |
||||||
|
|
||||||
|
@ExcelProperty("状态") |
||||||
|
private Integer status; |
||||||
|
|
||||||
|
@ExcelProperty("手机号") |
||||||
|
private String mobile; |
||||||
|
|
||||||
|
@ExcelProperty("创建时间") |
||||||
|
private Date createTime; |
||||||
|
|
||||||
|
@ExcelProperty("姓名") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ExcelProperty("部门") |
||||||
|
private Object department; |
||||||
|
|
||||||
|
@ExcelProperty("职位") |
||||||
|
private String position; |
||||||
|
|
||||||
|
@ExcelProperty("邮件") |
||||||
|
private String email; |
||||||
|
|
||||||
|
@ExcelProperty("企业邮件") |
||||||
|
private String bizMail; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
||||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
||||||
|
|
||||||
|
@ApiModel(value = "管理后台 - 企业微信成员 Excel 导出 Request VO", description = "参数和 CpUserPageReqVO 是一致的") |
||||||
|
@Data |
||||||
|
public class CpUserExportReqVO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户昵称") |
||||||
|
private String userid; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "头像") |
||||||
|
private String avatar; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "状态") |
||||||
|
private Integer status; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "手机号") |
||||||
|
private String mobile; |
||||||
|
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
||||||
|
@ApiModelProperty(value = "开始创建时间") |
||||||
|
private Date beginCreateTime; |
||||||
|
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
||||||
|
@ApiModelProperty(value = "结束创建时间") |
||||||
|
private Date endCreateTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "姓名") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "部门") |
||||||
|
private Object department; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "职位") |
||||||
|
private String position; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "邮件") |
||||||
|
private String email; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "企业邮件") |
||||||
|
private String bizMail; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
||||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
||||||
|
|
||||||
|
@ApiModel("管理后台 - 企业微信成员分页 Request VO") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ToString(callSuper = true) |
||||||
|
public class CpUserPageReqVO extends PageParam { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户昵称") |
||||||
|
private String userid; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "头像") |
||||||
|
private String avatar; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "状态") |
||||||
|
private Integer status; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "手机号") |
||||||
|
private String mobile; |
||||||
|
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
||||||
|
@ApiModelProperty(value = "开始创建时间") |
||||||
|
private Date beginCreateTime; |
||||||
|
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
||||||
|
@ApiModelProperty(value = "结束创建时间") |
||||||
|
private Date endCreateTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "姓名") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "部门") |
||||||
|
private Object department; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "职位") |
||||||
|
private String position; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "邮件") |
||||||
|
private String email; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "企业邮件") |
||||||
|
private String bizMail; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
|
||||||
|
@ApiModel("管理后台 - 企业微信成员 Response VO") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ToString(callSuper = true) |
||||||
|
public class CpUserRespVO extends CpUserBaseVO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true) |
||||||
|
private Long id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间", required = true) |
||||||
|
private Date createTime; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.CpUser.vo; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import io.swagger.annotations.*; |
||||||
|
import javax.validation.constraints.*; |
||||||
|
|
||||||
|
@ApiModel("管理后台 - 企业微信成员更新 Request VO") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ToString(callSuper = true) |
||||||
|
public class CpUserUpdateReqVO extends CpUserBaseVO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true) |
||||||
|
@NotNull(message = "编号不能为空") |
||||||
|
private Long id; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.convert.CpUser; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
|
||||||
|
import me.chanjar.weixin.cp.bean.WxCpUser; |
||||||
|
import org.mapstruct.Mapper; |
||||||
|
import org.mapstruct.factory.Mappers; |
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Convert |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@Mapper |
||||||
|
public interface CpUserConvert { |
||||||
|
|
||||||
|
CpUserConvert INSTANCE = Mappers.getMapper(CpUserConvert.class); |
||||||
|
|
||||||
|
CpUserDO convert(CpUserCreateReqVO bean); |
||||||
|
|
||||||
|
CpUserDO convert(CpUserUpdateReqVO bean); |
||||||
|
|
||||||
|
CpUserRespVO convert(CpUserDO bean); |
||||||
|
|
||||||
|
List<CpUserRespVO> convertList(List<CpUserDO> list); |
||||||
|
|
||||||
|
PageResult<CpUserRespVO> convertPage(PageResult<CpUserDO> page); |
||||||
|
|
||||||
|
List<CpUserExcelVO> convertList02(List<CpUserDO> list); |
||||||
|
|
||||||
|
List<CpUserDO> convertListFromWxApi(List<WxCpUser> list); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.dal.dataobject.CpUser; |
||||||
|
|
||||||
|
import lombok.*; |
||||||
|
import java.util.*; |
||||||
|
import com.baomidou.mybatisplus.annotation.*; |
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 DO |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@TableName("wxcp_users") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ToString(callSuper = true) |
||||||
|
@Builder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class CpUserDO extends BaseDO { |
||||||
|
|
||||||
|
/** |
||||||
|
* 编号 |
||||||
|
*/ |
||||||
|
@TableId |
||||||
|
private Long id; |
||||||
|
/** |
||||||
|
* 用户昵称 |
||||||
|
*/ |
||||||
|
@TableField(value = "user_id") |
||||||
|
private String userId; |
||||||
|
/** |
||||||
|
* 头像 |
||||||
|
*/ |
||||||
|
private String avatar; |
||||||
|
/** |
||||||
|
* 状态 |
||||||
|
*/ |
||||||
|
private Integer status; |
||||||
|
/** |
||||||
|
* 手机号 |
||||||
|
*/ |
||||||
|
private String mobile; |
||||||
|
/** |
||||||
|
* 姓名 |
||||||
|
*/ |
||||||
|
private String name; |
||||||
|
/** |
||||||
|
* 部门 |
||||||
|
*/ |
||||||
|
private Object department; |
||||||
|
/** |
||||||
|
* 职位 |
||||||
|
*/ |
||||||
|
private String position; |
||||||
|
/** |
||||||
|
* 邮件 |
||||||
|
*/ |
||||||
|
private String email; |
||||||
|
/** |
||||||
|
* 企业邮件 |
||||||
|
*/ |
||||||
|
private String bizMail; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.dal.mysql.CpUser; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
import org.apache.ibatis.annotations.Mapper; |
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Mapper |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@Mapper |
||||||
|
public interface CpUserMapper extends BaseMapperX<CpUserDO> { |
||||||
|
|
||||||
|
default PageResult<CpUserDO> selectPage(CpUserPageReqVO reqVO) { |
||||||
|
return selectPage(reqVO, new LambdaQueryWrapperX<CpUserDO>() |
||||||
|
.eqIfPresent(CpUserDO::getUserId, reqVO.getUserid()) |
||||||
|
.eqIfPresent(CpUserDO::getAvatar, reqVO.getAvatar()) |
||||||
|
.eqIfPresent(CpUserDO::getStatus, reqVO.getStatus()) |
||||||
|
.eqIfPresent(CpUserDO::getMobile, reqVO.getMobile()) |
||||||
|
.betweenIfPresent(CpUserDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) |
||||||
|
.likeIfPresent(CpUserDO::getName, reqVO.getName()) |
||||||
|
.eqIfPresent(CpUserDO::getDepartment, reqVO.getDepartment()) |
||||||
|
.eqIfPresent(CpUserDO::getPosition, reqVO.getPosition()) |
||||||
|
.eqIfPresent(CpUserDO::getEmail, reqVO.getEmail()) |
||||||
|
.eqIfPresent(CpUserDO::getBizMail, reqVO.getBizMail()) |
||||||
|
.orderByDesc(CpUserDO::getId)); |
||||||
|
} |
||||||
|
|
||||||
|
default List<CpUserDO> selectList(CpUserExportReqVO reqVO) { |
||||||
|
return selectList(new LambdaQueryWrapperX<CpUserDO>() |
||||||
|
.eqIfPresent(CpUserDO::getUserId, reqVO.getUserid()) |
||||||
|
.eqIfPresent(CpUserDO::getAvatar, reqVO.getAvatar()) |
||||||
|
.eqIfPresent(CpUserDO::getStatus, reqVO.getStatus()) |
||||||
|
.eqIfPresent(CpUserDO::getMobile, reqVO.getMobile()) |
||||||
|
.betweenIfPresent(CpUserDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) |
||||||
|
.likeIfPresent(CpUserDO::getName, reqVO.getName()) |
||||||
|
.eqIfPresent(CpUserDO::getDepartment, reqVO.getDepartment()) |
||||||
|
.eqIfPresent(CpUserDO::getPosition, reqVO.getPosition()) |
||||||
|
.eqIfPresent(CpUserDO::getEmail, reqVO.getEmail()) |
||||||
|
.eqIfPresent(CpUserDO::getBizMail, reqVO.getBizMail()) |
||||||
|
.orderByDesc(CpUserDO::getId)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.job.cpuser; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
||||||
|
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; |
||||||
|
import cn.iocoder.yudao.module.system.service.CpUser.CpUserService; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
|
||||||
|
@Component |
||||||
|
@TenantJob |
||||||
|
@Slf4j |
||||||
|
public class CpWeixinUserSyncJob implements JobHandler { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private CpUserService cpUserService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String execute(String param) throws Exception { |
||||||
|
cpUserService.cpUserSync(); |
||||||
|
return "企业微信同步完成..."; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.service.CpUser; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
import javax.validation.*; |
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||||
|
import me.chanjar.weixin.common.error.WxErrorException; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Service 接口 |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
public interface CpUserService extends IService<CpUserDO> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建企业微信成员 |
||||||
|
* |
||||||
|
* @param createReqVO 创建信息 |
||||||
|
* @return 编号 |
||||||
|
*/ |
||||||
|
Long createCpUser(@Valid CpUserCreateReqVO createReqVO); |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新企业微信成员 |
||||||
|
* |
||||||
|
* @param updateReqVO 更新信息 |
||||||
|
*/ |
||||||
|
void updateCpUser(@Valid CpUserUpdateReqVO updateReqVO); |
||||||
|
|
||||||
|
/** |
||||||
|
* 删除企业微信成员 |
||||||
|
* |
||||||
|
* @param id 编号 |
||||||
|
*/ |
||||||
|
void deleteCpUser(Long id); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获得企业微信成员 |
||||||
|
* |
||||||
|
* @param id 编号 |
||||||
|
* @return 企业微信成员 |
||||||
|
*/ |
||||||
|
CpUserDO getCpUser(Long id); |
||||||
|
|
||||||
|
CpUserDO getByUserId(String userId); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获得企业微信成员列表 |
||||||
|
* |
||||||
|
* @param ids 编号 |
||||||
|
* @return 企业微信成员列表 |
||||||
|
*/ |
||||||
|
List<CpUserDO> getCpUserList(Collection<Long> ids); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获得企业微信成员分页 |
||||||
|
* |
||||||
|
* @param pageReqVO 分页查询 |
||||||
|
* @return 企业微信成员分页 |
||||||
|
*/ |
||||||
|
PageResult<CpUserDO> getCpUserPage(CpUserPageReqVO pageReqVO); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获得企业微信成员列表, 用于 Excel 导出 |
||||||
|
* |
||||||
|
* @param exportReqVO 查询条件 |
||||||
|
* @return 企业微信成员列表 |
||||||
|
*/ |
||||||
|
List<CpUserDO> getCpUserList(CpUserExportReqVO exportReqVO); |
||||||
|
|
||||||
|
void cpUserSync() throws WxErrorException; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.service.CpUser; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import me.chanjar.weixin.common.error.WxErrorException; |
||||||
|
import me.chanjar.weixin.cp.api.WxCpService; |
||||||
|
import me.chanjar.weixin.cp.bean.WxCpUser; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
import org.springframework.validation.annotation.Validated; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.system.convert.CpUser.CpUserConvert; |
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.CpUser.CpUserMapper; |
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* 企业微信成员 Service 实现类 |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
@Validated |
||||||
|
@Slf4j |
||||||
|
public class CpUserServiceImpl extends ServiceImpl<CpUserMapper,CpUserDO> implements CpUserService { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private CpUserMapper cpUserMapper; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private WxCpService wxCpService; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Long createCpUser(CpUserCreateReqVO createReqVO) { |
||||||
|
// 插入
|
||||||
|
CpUserDO cpUser = CpUserConvert.INSTANCE.convert(createReqVO); |
||||||
|
cpUserMapper.insert(cpUser); |
||||||
|
// 返回
|
||||||
|
return cpUser.getId(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateCpUser(CpUserUpdateReqVO updateReqVO) { |
||||||
|
// 校验存在
|
||||||
|
this.validateCpUserExists(updateReqVO.getId()); |
||||||
|
// 更新
|
||||||
|
CpUserDO updateObj = CpUserConvert.INSTANCE.convert(updateReqVO); |
||||||
|
cpUserMapper.updateById(updateObj); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void deleteCpUser(Long id) { |
||||||
|
// 校验存在
|
||||||
|
this.validateCpUserExists(id); |
||||||
|
// 删除
|
||||||
|
cpUserMapper.deleteById(id); |
||||||
|
} |
||||||
|
|
||||||
|
private void validateCpUserExists(Long id) { |
||||||
|
if (cpUserMapper.selectById(id) == null) { |
||||||
|
throw exception(CP_USER_NOT_EXISTS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CpUserDO getCpUser(Long id) { |
||||||
|
return cpUserMapper.selectById(id); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CpUserDO getByUserId(String userId) { |
||||||
|
return this.getOne(Wrappers.<CpUserDO>lambdaQuery().eq(CpUserDO::getUserId,userId),false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<CpUserDO> getCpUserList(Collection<Long> ids) { |
||||||
|
return cpUserMapper.selectBatchIds(ids); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public PageResult<CpUserDO> getCpUserPage(CpUserPageReqVO pageReqVO) { |
||||||
|
return cpUserMapper.selectPage(pageReqVO); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<CpUserDO> getCpUserList(CpUserExportReqVO exportReqVO) { |
||||||
|
return cpUserMapper.selectList(exportReqVO); |
||||||
|
} |
||||||
|
|
||||||
|
public void cpUserSync() throws WxErrorException { |
||||||
|
List<WxCpUser> userList = wxCpService.getUserService().listByDepartment(1L, true, 0); |
||||||
|
|
||||||
|
List<CpUserDO> rs = CpUserConvert.INSTANCE.convertListFromWxApi(userList); |
||||||
|
|
||||||
|
List<CpUserDO> nowUsers = this.cpUserMapper.selectList(); |
||||||
|
|
||||||
|
// 已经存在的员工
|
||||||
|
List<String> nowUsersIds = nowUsers.stream().map(CpUserDO::getUserId).collect(Collectors.toList()); |
||||||
|
|
||||||
|
rs.removeIf(cpUserDO -> nowUsersIds.contains(cpUserDO.getUserId())); |
||||||
|
|
||||||
|
this.saveBatch(rs); |
||||||
|
|
||||||
|
// 已经离职的员工 设置删除
|
||||||
|
List<String> leaveUserIds = userList.stream().filter(wxCpUser -> wxCpUser.getStatus() == 0) |
||||||
|
.map(WxCpUser::getUserId).collect(Collectors.toList()); |
||||||
|
|
||||||
|
List<CpUserDO> leaveUsers = this.list(Wrappers.<CpUserDO>lambdaQuery() |
||||||
|
.in(CpUserDO::getUserId, leaveUserIds)); |
||||||
|
leaveUsers.forEach(cpUserDO -> cpUserDO.setDeleted(true)); |
||||||
|
this.updateBatchById(leaveUsers); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||||
|
<mapper namespace="cn.iocoder.yudao.module.system.dal.mysql.CpUser.CpUserMapper"> |
||||||
|
|
||||||
|
<!-- |
||||||
|
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 |
||||||
|
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。 |
||||||
|
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。 |
||||||
|
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ |
||||||
|
--> |
||||||
|
|
||||||
|
</mapper> |
@ -0,0 +1,218 @@ |
|||||||
|
package cn.iocoder.yudao.module.system.service.CpUser; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.CpUser.vo.*; |
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.CpUser.CpUserDO; |
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.CpUser.CpUserMapper; |
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
||||||
|
|
||||||
|
import org.springframework.context.annotation.Import; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; |
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
||||||
|
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; |
||||||
|
import static org.junit.jupiter.api.Assertions.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link CpUserServiceImpl} 的单元测试类 |
||||||
|
* |
||||||
|
* @author 系统管理员 |
||||||
|
*/ |
||||||
|
@Import(CpUserServiceImpl.class) |
||||||
|
public class CpUserServiceImplTest extends BaseDbUnitTest { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private CpUserServiceImpl cpUserService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private CpUserMapper cpUserMapper; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCreateCpUser_success() { |
||||||
|
// 准备参数
|
||||||
|
CpUserCreateReqVO reqVO = randomPojo(CpUserCreateReqVO.class); |
||||||
|
|
||||||
|
// 调用
|
||||||
|
Long cpUserId = cpUserService.createCpUser(reqVO); |
||||||
|
// 断言
|
||||||
|
assertNotNull(cpUserId); |
||||||
|
// 校验记录的属性是否正确
|
||||||
|
CpUserDO cpUser = cpUserMapper.selectById(cpUserId); |
||||||
|
assertPojoEquals(reqVO, cpUser); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testUpdateCpUser_success() { |
||||||
|
// mock 数据
|
||||||
|
CpUserDO dbCpUser = randomPojo(CpUserDO.class); |
||||||
|
cpUserMapper.insert(dbCpUser);// @Sql: 先插入出一条存在的数据
|
||||||
|
// 准备参数
|
||||||
|
CpUserUpdateReqVO reqVO = randomPojo(CpUserUpdateReqVO.class, o -> { |
||||||
|
o.setId(dbCpUser.getId()); // 设置更新的 ID
|
||||||
|
}); |
||||||
|
|
||||||
|
// 调用
|
||||||
|
cpUserService.updateCpUser(reqVO); |
||||||
|
// 校验是否更新正确
|
||||||
|
CpUserDO cpUser = cpUserMapper.selectById(reqVO.getId()); // 获取最新的
|
||||||
|
assertPojoEquals(reqVO, cpUser); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testUpdateCpUser_notExists() { |
||||||
|
// 准备参数
|
||||||
|
CpUserUpdateReqVO reqVO = randomPojo(CpUserUpdateReqVO.class); |
||||||
|
|
||||||
|
// 调用, 并断言异常
|
||||||
|
assertServiceException(() -> cpUserService.updateCpUser(reqVO), CP_USER_NOT_EXISTS); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDeleteCpUser_success() { |
||||||
|
// mock 数据
|
||||||
|
CpUserDO dbCpUser = randomPojo(CpUserDO.class); |
||||||
|
cpUserMapper.insert(dbCpUser);// @Sql: 先插入出一条存在的数据
|
||||||
|
// 准备参数
|
||||||
|
Long id = dbCpUser.getId(); |
||||||
|
|
||||||
|
// 调用
|
||||||
|
cpUserService.deleteCpUser(id); |
||||||
|
// 校验数据不存在了
|
||||||
|
assertNull(cpUserMapper.selectById(id)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDeleteCpUser_notExists() { |
||||||
|
// 准备参数
|
||||||
|
Long id = randomLongId(); |
||||||
|
|
||||||
|
// 调用, 并断言异常
|
||||||
|
assertServiceException(() -> cpUserService.deleteCpUser(id), CP_USER_NOT_EXISTS); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||||
|
public void testGetCpUserPage() { |
||||||
|
// mock 数据
|
||||||
|
CpUserDO dbCpUser = randomPojo(CpUserDO.class, o -> { // 等会查询到
|
||||||
|
o.setUserId(null); |
||||||
|
o.setAvatar(null); |
||||||
|
o.setStatus(null); |
||||||
|
o.setMobile(null); |
||||||
|
o.setCreateTime(null); |
||||||
|
o.setName(null); |
||||||
|
o.setDepartment(null); |
||||||
|
o.setPosition(null); |
||||||
|
o.setEmail(null); |
||||||
|
o.setBizMail(null); |
||||||
|
}); |
||||||
|
cpUserMapper.insert(dbCpUser); |
||||||
|
// 测试 userid 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setUserId(null))); |
||||||
|
// 测试 avatar 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setAvatar(null))); |
||||||
|
// 测试 status 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setStatus(null))); |
||||||
|
// 测试 mobile 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setMobile(null))); |
||||||
|
// 测试 createTime 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setCreateTime(null))); |
||||||
|
// 测试 name 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setName(null))); |
||||||
|
// 测试 department 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setDepartment(null))); |
||||||
|
// 测试 position 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setPosition(null))); |
||||||
|
// 测试 email 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setEmail(null))); |
||||||
|
// 测试 bizMail 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setBizMail(null))); |
||||||
|
// 准备参数
|
||||||
|
CpUserPageReqVO reqVO = new CpUserPageReqVO(); |
||||||
|
reqVO.setUserid(null); |
||||||
|
reqVO.setAvatar(null); |
||||||
|
reqVO.setStatus(null); |
||||||
|
reqVO.setMobile(null); |
||||||
|
reqVO.setBeginCreateTime(null); |
||||||
|
reqVO.setEndCreateTime(null); |
||||||
|
reqVO.setName(null); |
||||||
|
reqVO.setDepartment(null); |
||||||
|
reqVO.setPosition(null); |
||||||
|
reqVO.setEmail(null); |
||||||
|
reqVO.setBizMail(null); |
||||||
|
|
||||||
|
// 调用
|
||||||
|
PageResult<CpUserDO> pageResult = cpUserService.getCpUserPage(reqVO); |
||||||
|
// 断言
|
||||||
|
assertEquals(1, pageResult.getTotal()); |
||||||
|
assertEquals(1, pageResult.getList().size()); |
||||||
|
assertPojoEquals(dbCpUser, pageResult.getList().get(0)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||||
|
public void testGetCpUserList() { |
||||||
|
// mock 数据
|
||||||
|
CpUserDO dbCpUser = randomPojo(CpUserDO.class, o -> { // 等会查询到
|
||||||
|
o.setUserId(null); |
||||||
|
o.setAvatar(null); |
||||||
|
o.setStatus(null); |
||||||
|
o.setMobile(null); |
||||||
|
o.setCreateTime(null); |
||||||
|
o.setName(null); |
||||||
|
o.setDepartment(null); |
||||||
|
o.setPosition(null); |
||||||
|
o.setEmail(null); |
||||||
|
o.setBizMail(null); |
||||||
|
}); |
||||||
|
cpUserMapper.insert(dbCpUser); |
||||||
|
// 测试 userid 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setUserId(null))); |
||||||
|
// 测试 avatar 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setAvatar(null))); |
||||||
|
// 测试 status 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setStatus(null))); |
||||||
|
// 测试 mobile 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setMobile(null))); |
||||||
|
// 测试 createTime 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setCreateTime(null))); |
||||||
|
// 测试 name 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setName(null))); |
||||||
|
// 测试 department 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setDepartment(null))); |
||||||
|
// 测试 position 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setPosition(null))); |
||||||
|
// 测试 email 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setEmail(null))); |
||||||
|
// 测试 bizMail 不匹配
|
||||||
|
cpUserMapper.insert(cloneIgnoreId(dbCpUser, o -> o.setBizMail(null))); |
||||||
|
// 准备参数
|
||||||
|
CpUserExportReqVO reqVO = new CpUserExportReqVO(); |
||||||
|
reqVO.setUserid(null); |
||||||
|
reqVO.setAvatar(null); |
||||||
|
reqVO.setStatus(null); |
||||||
|
reqVO.setMobile(null); |
||||||
|
reqVO.setBeginCreateTime(null); |
||||||
|
reqVO.setEndCreateTime(null); |
||||||
|
reqVO.setName(null); |
||||||
|
reqVO.setDepartment(null); |
||||||
|
reqVO.setPosition(null); |
||||||
|
reqVO.setEmail(null); |
||||||
|
reqVO.setBizMail(null); |
||||||
|
|
||||||
|
// 调用
|
||||||
|
List<CpUserDO> list = cpUserService.getCpUserList(reqVO); |
||||||
|
// 断言
|
||||||
|
assertEquals(1, list.size()); |
||||||
|
assertPojoEquals(dbCpUser, list.get(0)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
server: |
||||||
|
port: 48080 |
||||||
|
|
||||||
|
#################### 数据库相关配置 #################### |
||||||
|
|
||||||
|
spring: |
||||||
|
# 数据源配置项 |
||||||
|
autoconfigure: |
||||||
|
exclude: |
||||||
|
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 |
||||||
|
|
||||||
|
datasource: |
||||||
|
druid: # Druid 【监控】相关的全局配置 |
||||||
|
web-stat-filter: |
||||||
|
enabled: true |
||||||
|
stat-view-servlet: |
||||||
|
enabled: true |
||||||
|
allow: # 设置白名单,不填则允许所有访问 |
||||||
|
url-pattern: /druid/* |
||||||
|
login-username: # 控制台管理用户名和密码 |
||||||
|
login-password: |
||||||
|
filter: |
||||||
|
stat: |
||||||
|
enabled: true |
||||||
|
log-slow-sql: true # 慢 SQL 记录 |
||||||
|
slow-sql-millis: 100 |
||||||
|
merge-sql: true |
||||||
|
wall: |
||||||
|
config: |
||||||
|
multi-statement-allow: true |
||||||
|
|
||||||
|
dynamic: # 多数据源配置 |
||||||
|
druid: # Druid 【连接池】相关的全局配置 |
||||||
|
initial-size: 5 # 初始连接数 |
||||||
|
min-idle: 10 # 最小连接池数量 |
||||||
|
max-active: 20 # 最大连接池数量 |
||||||
|
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 |
||||||
|
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 |
||||||
|
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 |
||||||
|
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 |
||||||
|
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 |
||||||
|
test-while-idle: true |
||||||
|
test-on-borrow: false |
||||||
|
test-on-return: false |
||||||
|
primary: master |
||||||
|
datasource: |
||||||
|
master: |
||||||
|
name: ruoyi-vue-pro |
||||||
|
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT |
||||||
|
driver-class-name: com.mysql.jdbc.Driver |
||||||
|
username: root |
||||||
|
password: cyjj123! |
||||||
|
slave: # 模拟从库,可根据自己需要修改 |
||||||
|
name: ruoyi-vue-pro |
||||||
|
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT |
||||||
|
driver-class-name: com.mysql.jdbc.Driver |
||||||
|
username: root |
||||||
|
password: cyjj123! |
||||||
|
bxg: # 农场数据源 |
||||||
|
name: bxg |
||||||
|
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.bxg.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT |
||||||
|
driver-class-name: com.mysql.jdbc.Driver |
||||||
|
username: root |
||||||
|
password: cyjj123! |
||||||
|
# farm: # 农场数据源 |
||||||
|
# name: zsw-farm |
||||||
|
# url: jdbc:mysql://192.168.10.129:3306/${spring.datasource.dynamic.datasource.farm.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT |
||||||
|
# driver-class-name: com.mysql.jdbc.Driver |
||||||
|
# username: root |
||||||
|
# password: root |
||||||
|
|
||||||
|
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
||||||
|
redis: |
||||||
|
host: 127.0.0.1 # 地址 |
||||||
|
port: 6379 # 端口 |
||||||
|
database: 15 # 数据库索引 |
||||||
|
|
||||||
|
--- #################### 定时任务相关配置 #################### |
||||||
|
|
||||||
|
# Quartz 配置项,对应 QuartzProperties 配置类 |
||||||
|
spring: |
||||||
|
quartz: |
||||||
|
auto-startup: false # 本地开发环境,尽量不要开启 Job |
||||||
|
scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName |
||||||
|
job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 |
||||||
|
wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true |
||||||
|
properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 |
||||||
|
org: |
||||||
|
quartz: |
||||||
|
# Scheduler 相关配置 |
||||||
|
scheduler: |
||||||
|
instanceName: schedulerName |
||||||
|
instanceId: AUTO # 自动生成 instance ID |
||||||
|
# JobStore 相关配置 |
||||||
|
jobStore: |
||||||
|
# JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162 |
||||||
|
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore |
||||||
|
isClustered: true # 是集群模式 |
||||||
|
clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒 |
||||||
|
misfireThreshold: 60000 # misfire 阀值,单位:毫秒。 |
||||||
|
# 线程池相关配置 |
||||||
|
threadPool: |
||||||
|
threadCount: 25 # 线程池大小。默认为 10 。 |
||||||
|
threadPriority: 5 # 线程优先级 |
||||||
|
class: org.quartz.simpl.SimpleThreadPool # 线程池类型 |
||||||
|
jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 |
||||||
|
initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 |
||||||
|
|
||||||
|
--- #################### 配置中心相关配置 #################### |
||||||
|
|
||||||
|
# Apollo 配置中心 |
||||||
|
apollo: |
||||||
|
bootstrap: |
||||||
|
enabled: true # 设置 Apollo 在启动阶段生效 |
||||||
|
eagerLoad: |
||||||
|
enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置 |
||||||
|
jdbc: # 自定义的 JDBC 配置项,用于数据库的地址 |
||||||
|
dao: cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigDAOImpl |
||||||
|
url: ${spring.datasource.dynamic.datasource.master.url} |
||||||
|
username: ${spring.datasource.dynamic.datasource.master.username} |
||||||
|
password: ${spring.datasource.dynamic.datasource.master.password} |
||||||
|
|
||||||
|
#################### 服务保障相关配置 #################### |
||||||
|
|
||||||
|
# Lock4j 配置项 |
||||||
|
lock4j: |
||||||
|
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 |
||||||
|
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 |
||||||
|
|
||||||
|
# Resilience4j 配置项 |
||||||
|
resilience4j: |
||||||
|
ratelimiter: |
||||||
|
instances: |
||||||
|
backendA: |
||||||
|
limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 |
||||||
|
limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500 |
||||||
|
timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s |
||||||
|
register-health-indicator: true # 是否注册到健康监测 |
||||||
|
|
||||||
|
#################### 监控相关配置 #################### |
||||||
|
|
||||||
|
# Actuator 监控端点的配置项 |
||||||
|
management: |
||||||
|
endpoints: |
||||||
|
web: |
||||||
|
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator |
||||||
|
exposure: |
||||||
|
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 |
||||||
|
|
||||||
|
# Spring Boot Admin 配置项 |
||||||
|
spring: |
||||||
|
boot: |
||||||
|
admin: |
||||||
|
# Spring Boot Admin Client 客户端的相关配置 |
||||||
|
client: |
||||||
|
url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 |
||||||
|
instance: |
||||||
|
prefer-ip: true # 注册实例时,优先使用 IP |
||||||
|
# Spring Boot Admin Server 服务端的相关配置 |
||||||
|
context-path: /admin # 配置 Spring |
||||||
|
|
||||||
|
# 日志文件配置 |
||||||
|
logging: |
||||||
|
file: |
||||||
|
name: logs/${spring.application.name}.log # 日志文件名,全路径 |
||||||
|
level: |
||||||
|
# 配置自己写的 MyBatis Mapper 打印日志 |
||||||
|
cn.iocoder.yudao.module.bpm.dal.mysql: debug |
||||||
|
cn.iocoder.yudao.module.infra.dal.mysql: debug |
||||||
|
cn.iocoder.yudao.module.pay.dal.mysql: debug |
||||||
|
cn.iocoder.yudao.module.system.dal.mysql: debug |
||||||
|
cn.iocoder.yudao.module.tool.dal.mysql: debug |
||||||
|
cn.iocoder.yudao.module.member.dal.mysql: debug |
||||||
|
co.yixiang: debug |
||||||
|
|
||||||
|
--- #################### 微信公众号相关配置 #################### |
||||||
|
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 |
||||||
|
mp: |
||||||
|
# 公众号配置(必填) |
||||||
|
app-id: wx041349c6f39b268b |
||||||
|
secret: 5abee519483bc9f8cb37ce280e814bd0 |
||||||
|
# 存储配置,解决 AccessToken 的跨节点的共享 |
||||||
|
config-storage: |
||||||
|
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 |
||||||
|
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 |
||||||
|
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 |
||||||
|
|
||||||
|
--- #################### 芋道相关配置 #################### |
||||||
|
|
||||||
|
# 芋道配置项,设置当前项目所有自定义的配置 |
||||||
|
yudao: |
||||||
|
captcha: |
||||||
|
enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 |
||||||
|
security: |
||||||
|
token-header: Authorization |
||||||
|
token-timeout: 1d |
||||||
|
session-timeout: 1d |
||||||
|
mock-enable: true |
||||||
|
mock-secret: test |
||||||
|
xss: |
||||||
|
enable: false |
||||||
|
exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 |
||||||
|
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
||||||
|
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
||||||
|
pay: |
||||||
|
pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify |
||||||
|
pay-return-url: http://niubi.natapp1.cc/api/pay/order/return |
||||||
|
refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify |
||||||
|
demo: false # 关闭演示模式 |
||||||
|
|
||||||
|
justauth: |
||||||
|
enabled: true |
||||||
|
type: |
||||||
|
GITEE: # Gitee |
||||||
|
client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1 |
||||||
|
client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9 |
||||||
|
ignore-check-redirect-uri: true |
||||||
|
DINGTALK: # 钉钉 |
||||||
|
client-id: dingvrnreaje3yqvzhxg |
||||||
|
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI |
||||||
|
ignore-check-redirect-uri: true |
||||||
|
WECHAT_ENTERPRISE: # 企业微信 |
||||||
|
client-id: wwd411c69a39ad2e54 |
||||||
|
client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw |
||||||
|
agent-id: 1000004 |
||||||
|
ignore-check-redirect-uri: true |
||||||
|
cache: |
||||||
|
type: REDIS |
||||||
|
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: |
||||||
|
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 |
@ -1 +1 @@ |
|||||||
Subproject commit 05fdb1d9e71965bbb19e9e35b8732c861d7eab94 |
Subproject commit 940f54e876f1c6ccc6e239c5990d34e83fb02b78 |
@ -0,0 +1,44 @@ |
|||||||
|
package co.yixiang.app.modules.product.rest; |
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64; |
||||||
|
import cn.hutool.core.codec.Base64Decoder; |
||||||
|
import cn.hutool.extra.qrcode.QrCodeUtil; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import javax.imageio.ImageIO; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
|
||||||
|
@RestController |
||||||
|
@RequestMapping("/qrcode") |
||||||
|
public class ImageController { |
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows |
||||||
|
@ApiOperation(value = "二维码生成~") |
||||||
|
@GetMapping( |
||||||
|
value = "/image/{code}.png", |
||||||
|
produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_PNG_VALUE} |
||||||
|
) |
||||||
|
public byte[] orderCode(@PathVariable String code){ |
||||||
|
// 判断是否是base64
|
||||||
|
String pass = Base64Decoder.decodeStr(code); |
||||||
|
//
|
||||||
|
if (code.equals(Base64.encode(pass))){ |
||||||
|
code = pass; |
||||||
|
} |
||||||
|
|
||||||
|
BufferedImage img = QrCodeUtil.generate(code, 180, 180); |
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||||
|
ImageIO.write(img,"png",out); |
||||||
|
return out.toByteArray(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package co.yixiang.app.modules.shop.rest; |
||||||
|
|
||||||
|
public class CallStack { |
||||||
|
|
||||||
|
public static void printCallStatck() { |
||||||
|
Throwable ex = new Throwable(); |
||||||
|
StackTraceElement[] stackElements = ex.getStackTrace(); |
||||||
|
if (stackElements != null) { |
||||||
|
for (int i = 0; i < stackElements.length; i++) { |
||||||
|
System.out.print(stackElements[i].getClassName()+"/t"); |
||||||
|
System.out.print(stackElements[i].getFileName()+"/t"); |
||||||
|
System.out.print(stackElements[i].getLineNumber()+"/t"); |
||||||
|
System.out.println(stackElements[i].getMethodName()); |
||||||
|
System.out.println("-----------------------------------"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package co.yixiang.config; |
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil; |
||||||
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.codec.digest.DigestUtils; |
||||||
|
import org.springframework.cache.Cache; |
||||||
|
import org.springframework.cache.annotation.CachingConfigurerSupport; |
||||||
|
import org.springframework.cache.interceptor.CacheErrorHandler; |
||||||
|
import org.springframework.cache.interceptor.KeyGenerator; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
@Configuration |
||||||
|
public class CachingConfigurer extends CachingConfigurerSupport { |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义缓存key生成策略,默认将使用该策略 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
@Override |
||||||
|
public KeyGenerator keyGenerator() { |
||||||
|
return (target, method, params) -> { |
||||||
|
Long tenantId = TenantContextHolder.getTenantId(); |
||||||
|
if (ObjectUtil.isNotEmpty(tenantId)){ |
||||||
|
return tenantId; |
||||||
|
} |
||||||
|
Map<String,Object> container = new HashMap<>(3); |
||||||
|
Class<?> targetClassClass = target.getClass(); |
||||||
|
// 类地址
|
||||||
|
container.put("class",targetClassClass.toGenericString()); |
||||||
|
// 方法名称
|
||||||
|
container.put("methodName",method.getName()); |
||||||
|
// 包名称
|
||||||
|
container.put("package",targetClassClass.getPackage()); |
||||||
|
// 参数列表
|
||||||
|
for (int i = 0; i < params.length; i++) { |
||||||
|
container.put(String.valueOf(i),params[i]); |
||||||
|
} |
||||||
|
// 转为JSON字符串
|
||||||
|
String jsonString = JSON.toJSONString(container); |
||||||
|
// 做SHA256 Hash计算,得到一个SHA256摘要作为Key
|
||||||
|
return DigestUtils.sha256Hex(jsonString); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
@Override |
||||||
|
public CacheErrorHandler errorHandler() { |
||||||
|
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
|
||||||
|
log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); |
||||||
|
return new CacheErrorHandler() { |
||||||
|
@Override |
||||||
|
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { |
||||||
|
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { |
||||||
|
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { |
||||||
|
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handleCacheClearError(RuntimeException e, Cache cache) { |
||||||
|
log.error("Redis occur handleCacheClearError:", e); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue