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.TableField; |
@ -1,137 +1,137 @@
|
||||
package cn.iocoder.yudao.module.member.service.user; |
||||
|
||||
import cn.hutool.core.util.RandomUtil; |
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; |
||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; |
||||
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.dal.dataobject.user.MemberUserDO; |
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
||||
import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl; |
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.boot.test.mock.mockito.MockBean; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
|
||||
import javax.annotation.Resource; |
||||
import java.io.ByteArrayInputStream; |
||||
import java.util.function.Consumer; |
||||
|
||||
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.randomString; |
||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
import static org.mockito.Mockito.eq; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
||||
/** |
||||
* {@link MemberUserServiceImpl} 的单元测试类 |
||||
* |
||||
* @author 宋天 |
||||
*/ |
||||
@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) |
||||
public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest { |
||||
|
||||
@Resource |
||||
private MemberUserServiceImpl memberUserService; |
||||
|
||||
@Resource |
||||
private StringRedisTemplate stringRedisTemplate; |
||||
|
||||
@Resource |
||||
private MemberUserMapper userMapper; |
||||
|
||||
@MockBean |
||||
private MemberAuthServiceImpl authService; |
||||
|
||||
@MockBean |
||||
private PasswordEncoder passwordEncoder; |
||||
|
||||
@MockBean |
||||
private SmsCodeApi smsCodeApi; |
||||
@MockBean |
||||
private FileApi fileApi; |
||||
|
||||
@Test |
||||
public void testUpdateNickName_success(){ |
||||
// mock 数据
|
||||
MemberUserDO userDO = randomUserDO(); |
||||
userMapper.insert(userDO); |
||||
|
||||
// 随机昵称
|
||||
String newNickName = randomString(); |
||||
|
||||
// 调用接口修改昵称
|
||||
memberUserService.updateUserNickname(userDO.getId(),newNickName); |
||||
// 查询新修改后的昵称
|
||||
String nickname = memberUserService.getUser(userDO.getId()).getNickname(); |
||||
// 断言
|
||||
assertEquals(newNickName,nickname); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpdateAvatar_success() throws Exception { |
||||
// mock 数据
|
||||
MemberUserDO dbUser = randomUserDO(); |
||||
userMapper.insert(dbUser); |
||||
|
||||
// 准备参数
|
||||
Long userId = dbUser.getId(); |
||||
byte[] avatarFileBytes = randomBytes(10); |
||||
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); |
||||
// mock 方法
|
||||
String avatar = randomString(); |
||||
when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar); |
||||
// 调用
|
||||
String str = memberUserService.updateUserAvatar(userId, avatarFile); |
||||
// 断言
|
||||
assertEquals(avatar, str); |
||||
} |
||||
|
||||
@Test |
||||
public void updateMobile_success(){ |
||||
// mock数据
|
||||
String oldMobile = randomNumbers(11); |
||||
MemberUserDO userDO = randomUserDO(); |
||||
userDO.setMobile(oldMobile); |
||||
userMapper.insert(userDO); |
||||
|
||||
// TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
||||
// 旧手机和旧验证码
|
||||
// SmsCodeDO codeDO = new SmsCodeDO();
|
||||
String oldCode = RandomUtil.randomString(4); |
||||
// codeDO.setMobile(userDO.getMobile());
|
||||
// codeDO.setCode(oldCode);
|
||||
// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
||||
// codeDO.setUsed(Boolean.FALSE);
|
||||
// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
||||
|
||||
// 更新手机号
|
||||
String newMobile = randomNumbers(11); |
||||
String newCode = randomNumbers(4); |
||||
AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO(); |
||||
reqVO.setMobile(newMobile); |
||||
reqVO.setCode(newCode); |
||||
reqVO.setOldMobile(oldMobile); |
||||
reqVO.setOldCode(oldCode); |
||||
memberUserService.updateUserMobile(userDO.getId(),reqVO); |
||||
|
||||
assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile); |
||||
} |
||||
|
||||
// ========== 随机对象 ==========
|
||||
|
||||
@SafeVarargs |
||||
private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) { |
||||
Consumer<MemberUserDO> consumer = (o) -> { |
||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
}; |
||||
return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers)); |
||||
} |
||||
|
||||
} |
||||
//package cn.iocoder.yudao.module.member.service.user;
|
||||
//
|
||||
//import cn.hutool.core.util.RandomUtil;
|
||||
//import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
//import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||
//import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||
//import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
//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.dal.dataobject.user.MemberUserDO;
|
||||
//import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||
//import cn.iocoder.yudao.module.member.service.auth.MemberAuthServiceImpl;
|
||||
//import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
//import org.springframework.context.annotation.Import;
|
||||
//import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
//import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
//
|
||||
//import javax.annotation.Resource;
|
||||
//import java.io.ByteArrayInputStream;
|
||||
//import java.util.function.Consumer;
|
||||
//
|
||||
//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.randomString;
|
||||
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
//import static org.mockito.Mockito.eq;
|
||||
//import static org.mockito.Mockito.when;
|
||||
//
|
||||
//// TODO @芋艿:单测的 review,等逻辑都达成一致后
|
||||
///**
|
||||
// * {@link MemberUserServiceImpl} 的单元测试类
|
||||
// *
|
||||
// * @author 宋天
|
||||
// */
|
||||
//@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class})
|
||||
//public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
//
|
||||
// @Resource
|
||||
// private MemberUserServiceImpl memberUserService;
|
||||
//
|
||||
// @Resource
|
||||
// private StringRedisTemplate stringRedisTemplate;
|
||||
//
|
||||
// @Resource
|
||||
// private MemberUserMapper userMapper;
|
||||
//
|
||||
// @MockBean
|
||||
// private MemberAuthServiceImpl authService;
|
||||
//
|
||||
// @MockBean
|
||||
// private PasswordEncoder passwordEncoder;
|
||||
//
|
||||
// @MockBean
|
||||
// private SmsCodeApi smsCodeApi;
|
||||
// @MockBean
|
||||
// private FileApi fileApi;
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateNickName_success(){
|
||||
// // mock 数据
|
||||
// MemberUserDO userDO = randomUserDO();
|
||||
// userMapper.insert(userDO);
|
||||
//
|
||||
// // 随机昵称
|
||||
// String newNickName = randomString();
|
||||
//
|
||||
// // 调用接口修改昵称
|
||||
// memberUserService.updateUserNickname(userDO.getId(),newNickName);
|
||||
// // 查询新修改后的昵称
|
||||
// String nickname = memberUserService.getUser(userDO.getId()).getNickname();
|
||||
// // 断言
|
||||
// assertEquals(newNickName,nickname);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateAvatar_success() throws Exception {
|
||||
// // mock 数据
|
||||
// MemberUserDO dbUser = randomUserDO();
|
||||
// userMapper.insert(dbUser);
|
||||
//
|
||||
// // 准备参数
|
||||
// Long userId = dbUser.getId();
|
||||
// byte[] avatarFileBytes = randomBytes(10);
|
||||
// ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
|
||||
// // mock 方法
|
||||
// String avatar = randomString();
|
||||
// when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar);
|
||||
// // 调用
|
||||
// String str = memberUserService.updateUserAvatar(userId, avatarFile);
|
||||
// // 断言
|
||||
// assertEquals(avatar, str);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void updateMobile_success(){
|
||||
// // mock数据
|
||||
// String oldMobile = randomNumbers(11);
|
||||
// MemberUserDO userDO = randomUserDO();
|
||||
// userDO.setMobile(oldMobile);
|
||||
// userMapper.insert(userDO);
|
||||
//
|
||||
// // TODO 芋艿:需要修复该单元测试,重构多模块带来的
|
||||
// // 旧手机和旧验证码
|
||||
//// SmsCodeDO codeDO = new SmsCodeDO();
|
||||
// String oldCode = RandomUtil.randomString(4);
|
||||
//// codeDO.setMobile(userDO.getMobile());
|
||||
//// codeDO.setCode(oldCode);
|
||||
//// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene());
|
||||
//// codeDO.setUsed(Boolean.FALSE);
|
||||
//// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
|
||||
//
|
||||
// // 更新手机号
|
||||
// String newMobile = randomNumbers(11);
|
||||
// String newCode = randomNumbers(4);
|
||||
// AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO();
|
||||
// reqVO.setMobile(newMobile);
|
||||
// reqVO.setCode(newCode);
|
||||
// reqVO.setOldMobile(oldMobile);
|
||||
// reqVO.setOldCode(oldCode);
|
||||
// memberUserService.updateUserMobile(userDO.getId(),reqVO);
|
||||
//
|
||||
// assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile);
|
||||
// }
|
||||
//
|
||||
// // ========== 随机对象 ==========
|
||||
//
|
||||
// @SafeVarargs
|
||||
// private static MemberUserDO randomUserDO(Consumer<MemberUserDO>... consumers) {
|
||||
// Consumer<MemberUserDO> consumer = (o) -> {
|
||||
// o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
// };
|
||||
// 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