Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogService.java
#	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogService.java
This commit is contained in:
YunaiV 2023-08-22 12:57:15 +08:00
commit 551a968449
55 changed files with 1050 additions and 545 deletions

View File

@ -0,0 +1,36 @@
create table member_group
(
id bigint auto_increment comment '编号' primary key,
name varchar(30) default '' not null comment '名称',
remark varchar(255) default '' not null comment '备注',
status tinyint default 0 not null comment '状态',
creator varchar(64) default '' null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) default '' null comment '更新者',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' not null comment '是否删除',
tenant_id bigint default 0 not null comment '租户编号'
)
comment '用户分组';
alter table member_user add column group_id bigint null comment '用户分组编号';
-- 菜单 SQL
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
VALUES ('用户分组', '', 2, 5, 2262, 'group', '', 'member/group/index', 0, 'MemberGroup');
-- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('用户分组查询', 'member:group:query', 3, 1, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('用户分组创建', 'member:group:create', 3, 2, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('用户分组更新', 'member:group:update', 3, 3, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('用户分组删除', 'member:group:delete', 3, 4, @parentId, '', '', '', 0);
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
VALUES ('用户分组导出', 'member:group:export', 3, 5, @parentId, '', '', '', 0);

View File

@ -0,0 +1,180 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.DefaultSigner;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import javax.validation.ConstraintViolationException;
import java.util.Date;
import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when;
/**
* @author jason
*/
public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
private final String privateKey = randomString();
protected AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> {
t.setServerUrl(randomURL());
t.setPrivateKey(privateKey);
t.setMode(MODE_PUBLIC_KEY);
t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
t.setAppCertContent("");
t.setAlipayPublicCertContent("");
t.setRootCertContent("");
});
@Mock
protected DefaultAlipayClient defaultAlipayClient;
private AbstractAlipayPayClient client;
public void setClient(AbstractAlipayPayClient client) {
this.client = client;
}
@Test
@DisplayName("支付宝 Client 初始化")
public void test_do_init() {
client.doInit();
DefaultAlipayClient realClient = (DefaultAlipayClient) ReflectUtil.getFieldValue(client, "client");
assertNotSame(defaultAlipayClient, realClient);
assertInstanceOf(DefaultSigner.class, realClient.getSigner());
assertEquals(privateKey, ((DefaultSigner) realClient.getSigner()).getPrivateKey());
}
@Test
@DisplayName("支付宝 Client 统一退款成功")
public void test_unified_refund_success() throws AlipayApiException {
// 准备返回对象
String notifyUrl = randomURL();
Date refundTime = randomDate();
String outRefundNo = randomString();
String outTradeNo = randomString();
Integer refundAmount = randomInteger();
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
o.setSubCode("");
o.setGmtRefundPay(refundTime);
});
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
assertEquals(outRefundNo, bizModel.getOutRequestNo());
assertEquals(outTradeNo, bizModel.getOutTradeNo());
assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
return true;
}))).thenReturn(response);
// 准备请求参数
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutRefundNo(outRefundNo);
o.setOutTradeNo(outTradeNo);
o.setNotifyUrl(notifyUrl);
o.setRefundPrice(refundAmount);
});
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
// 断言
assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
assertNull(resp.getChannelRefundNo());
assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
assertEquals(outRefundNo, resp.getOutRefundNo());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付宝 Client 统一退款,渠道返回失败")
public void test_unified_refund_channel_failed() throws AlipayApiException {
// 准备返回对象
String notifyUrl = randomURL();
String subCode = randomString();
String subMsg = randomString();
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
o.setSubCode(subCode);
o.setSubMsg(subMsg);
});
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
return true;
}))).thenReturn(response);
// 准备请求参数
String outRefundNo = randomString();
String outTradeNo = randomString();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutRefundNo(outRefundNo);
o.setOutTradeNo(outTradeNo);
o.setNotifyUrl(notifyUrl);
});
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
// 断言
assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());
assertNull(resp.getChannelRefundNo());
assertEquals(subCode, resp.getChannelErrorCode());
assertEquals(subMsg, resp.getChannelErrorMsg());
assertNull(resp.getSuccessTime());
assertEquals(outRefundNo, resp.getOutRefundNo());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付宝 Client 统一退款,参数校验不通过")
public void test_unified_refund_param_validate() {
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutTradeNo("");
o.setNotifyUrl(notifyUrl);
});
// 断言
assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
}
@Test
@DisplayName("支付宝 Client 统一退款,抛出业务异常")
public void test_unified_refund_throw_service_exception() throws AlipayApiException {
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
.thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
// 断言
assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
}
@Test
@DisplayName("支付宝 Client 统一退款,抛出系统异常")
public void test_unified_refund_throw_pay_exception() throws AlipayApiException {
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
.thenThrow(new RuntimeException("系统异常"));
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
// 断言
assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));
}
}

View File

@ -1,68 +1,44 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import javax.validation.ConstraintViolationException;
import java.util.Date;
import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when;
/**
* {@link AlipayQrPayClient} 单元测试
*
* @author jason
*/
public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
private final AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> {
t.setServerUrl(randomURL());
t.setMode(MODE_PUBLIC_KEY);
t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
t.setAppCertContent("");
t.setAlipayPublicCertContent("");
t.setRootCertContent("");
});
public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
@InjectMocks
AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
private AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
@Mock
private DefaultAlipayClient defaultAlipayClient;
@Test
public void test_do_init() {
client.doInit();
assertNotSame(defaultAlipayClient, ReflectUtil.getFieldValue(client, "defaultAlipayClient"));
@BeforeEach
public void setUp() {
setClient(client);
}
@Test
@ -176,119 +152,4 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
o.setBody(RandomUtil.randomString(32));
});
}
@Test
@DisplayName("支付包扫描退款成功")
public void test_unified_refund_success() throws AlipayApiException {
// 准备返回对象
String notifyUrl = randomURL();
Date refundTime = randomDate();
String outRefundNo = randomString();
String outTradeNo = randomString();
Integer refundAmount = randomInteger();
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
o.setSubCode("");
o.setGmtRefundPay(refundTime);
});
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
assertEquals(outRefundNo, bizModel.getOutRequestNo());
assertEquals(outTradeNo, bizModel.getOutTradeNo());
assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
return true;
}))).thenReturn(response);
// 准备请求参数
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutRefundNo(outRefundNo);
o.setOutTradeNo(outTradeNo);
o.setNotifyUrl(notifyUrl);
o.setRefundPrice(refundAmount);
});
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
// 断言
assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
assertNull(resp.getChannelRefundNo());
assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
assertEquals(outRefundNo, resp.getOutRefundNo());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付包扫描退款,渠道返回失败")
public void test_unified_refund_channel_failed() throws AlipayApiException {
// 准备返回对象
String notifyUrl = randomURL();
String subCode = randomString();
String subMsg = randomString();
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
o.setSubCode(subCode);
o.setSubMsg(subMsg);
});
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
return true;
}))).thenReturn(response);
// 准备请求参数
String outRefundNo = randomString();
String outTradeNo = randomString();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutRefundNo(outRefundNo);
o.setOutTradeNo(outTradeNo);
o.setNotifyUrl(notifyUrl);
});
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
// 断言
assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());
assertNull(resp.getChannelRefundNo());
assertEquals(subCode, resp.getChannelErrorCode());
assertEquals(subMsg, resp.getChannelErrorMsg());
assertNull(resp.getSuccessTime());
assertEquals(outRefundNo, resp.getOutRefundNo());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付包扫描退款,参数校验不通过")
public void test_unified_refund_param_validate() {
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setOutTradeNo("");
o.setNotifyUrl(notifyUrl);
});
// 断言
assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
}
@Test
@DisplayName("支付包扫描退款,抛出业务异常")
public void test_unified_refund_throw_service_exception() throws AlipayApiException {
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
.thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setNotifyUrl(notifyUrl);
});
// 断言
assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
}
@Test
@DisplayName("支付包扫描退款,抛出系统异常")
public void test_unified_refund_throw_pay_exception() throws AlipayApiException {
// mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
.thenThrow(new RuntimeException("系统异常"));
// 准备请求参数
String notifyUrl = randomURL();
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
o.setNotifyUrl(notifyUrl);
});
// 断言
assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));
}
}

View File

@ -24,9 +24,10 @@ public interface ErrorCodeConstants {
// ========== 用户收件地址 1004004000 ==========
ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在");
//========== 会员标签 1004006000 ==========
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1004006000, "会员标签不存在");
ErrorCode TAG_NAME_EXISTS = new ErrorCode(1004006001, "会员标签已经存在");
//========== 用户标签 1004006000 ==========
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1004006000, "用户标签不存在");
ErrorCode TAG_NAME_EXISTS = new ErrorCode(1004006001, "用户标签已经存在");
ErrorCode TAG_HAS_USER = new ErrorCode(1004006002, "用户标签下存在用户,无法删除");
//========== 积分配置 1004007000 ==========
@ -39,14 +40,20 @@ public interface ErrorCodeConstants {
//========== 签到配置 1004010000 ==========
//========== 会员等级 1004007000 ==========
ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1004007000, "会员等级不存在");
ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1004007001, "会员等级名称[{}]已被使用");
ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1004007002, "会员等级值[{}]已被[{}]使用");
ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1004007003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1004007004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
//========== 用户等级 1004011000 ==========
ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1004011000, "用户等级不存在");
ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1004011001, "用户等级名称[{}]已被使用");
ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1004011002, "用户等级值[{}]已被[{}]使用");
ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1004011003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1004011004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_HAS_USER = new ErrorCode(1004011005, "用户等级下存在用户,无法删除");
ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004011100, "用户等级记录不存在");
ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004011200, "用户经验记录不存在");
ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004011300, "用户等级调整原因不能为空");
//========== 用户分组 1004012000 ==========
ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1004012000, "用户分组不存在");
ErrorCode GROUP_HAS_USER = new ErrorCode(1004012001, "用户分组下存在用户,无法删除");
ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在");
ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在");
ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004007300, "会员等级调整原因不能为空");
}

View File

@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.member.controller.admin.group;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.*;
import cn.iocoder.yudao.module.member.convert.group.MemberGroupConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 用户分组")
@RestController
@RequestMapping("/member/group")
@Validated
public class MemberGroupController {
@Resource
private MemberGroupService groupService;
@PostMapping("/create")
@Operation(summary = "创建用户分组")
@PreAuthorize("@ss.hasPermission('member:group:create')")
public CommonResult<Long> createGroup(@Valid @RequestBody MemberGroupCreateReqVO createReqVO) {
return success(groupService.createGroup(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新用户分组")
@PreAuthorize("@ss.hasPermission('member:group:update')")
public CommonResult<Boolean> updateGroup(@Valid @RequestBody MemberGroupUpdateReqVO updateReqVO) {
groupService.updateGroup(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除用户分组")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('member:group:delete')")
public CommonResult<Boolean> deleteGroup(@RequestParam("id") Long id) {
groupService.deleteGroup(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得用户分组")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('member:group:query')")
public CommonResult<MemberGroupRespVO> getGroup(@RequestParam("id") Long id) {
MemberGroupDO group = groupService.getGroup(id);
return success(MemberGroupConvert.INSTANCE.convert(group));
}
@GetMapping("/list-all-simple")
@Operation(summary = "获取会员分组精简信息列表", description = "只包含被开启的会员分组,主要用于前端的下拉选项")
public CommonResult<List<MemberGroupSimpleRespVO>> getSimpleGroupList() {
// 获用户列表只要开启状态的
List<MemberGroupDO> list = groupService.getEnableGroupList();
return success(MemberGroupConvert.INSTANCE.convertSimpleList(list));
}
@GetMapping("/page")
@Operation(summary = "获得用户分组分页")
@PreAuthorize("@ss.hasPermission('member:group:query')")
public CommonResult<PageResult<MemberGroupRespVO>> getGroupPage(@Valid MemberGroupPageReqVO pageVO) {
PageResult<MemberGroupDO> pageResult = groupService.getGroupPage(pageVO);
return success(MemberGroupConvert.INSTANCE.convertPage(pageResult));
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 用户分组 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class MemberGroupBaseVO {
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "购物达人")
@NotNull(message = "名称不能为空")
private String name;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜")
private String remark;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 用户分组创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberGroupCreateReqVO extends MemberGroupBaseVO {
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 用户分组分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberGroupPageReqVO extends PageParam {
@Schema(description = "名称", example = "购物达人")
private String name;
@Schema(description = "状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 用户分组 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberGroupRespVO extends MemberGroupBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
@Schema(description = "管理后台 - 用户分组 Response VO")
@Data
@ToString(callSuper = true)
public class MemberGroupSimpleRespVO {
@Schema(description = "编号", example = "6103")
private Long id;
@Schema(description = "等级名称", example = "芋艿")
private String name;
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 用户分组更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberGroupUpdateReqVO extends MemberGroupBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357")
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.member.controller.admin.level;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExcelVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogRespVO;
import cn.iocoder.yudao.module.member.convert.level.MemberExperienceLogConvert;
@ -16,17 +12,15 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
// TODO @疯狂要不 Log 改成 Record PointRecord 保持一致
@Tag(name = "管理后台 - 会员经验记录")
@ -38,16 +32,6 @@ public class MemberExperienceLogController {
@Resource
private MemberExperienceLogService experienceLogService;
// TODO @疯狂不允许删除经验哈
@DeleteMapping("/delete")
@Operation(summary = "删除会员经验记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('member:experience-log:delete')")
public CommonResult<Boolean> deleteExperienceLog(@RequestParam("id") Long id) {
experienceLogService.deleteExperienceLog(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会员经验记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@ -57,16 +41,6 @@ public class MemberExperienceLogController {
return success(MemberExperienceLogConvert.INSTANCE.convert(experienceLog));
}
// TODO @疯狂这个接口可以删除哈应该用不到
@GetMapping("/list")
@Operation(summary = "获得会员经验记录列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('member:experience-log:query')")
public CommonResult<List<MemberExperienceLogRespVO>> getExperienceLogList(@RequestParam("ids") Collection<Long> ids) {
List<MemberExperienceLogDO> list = experienceLogService.getExperienceLogList(ids);
return success(MemberExperienceLogConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得会员经验记录分页")
@PreAuthorize("@ss.hasPermission('member:experience-log:query')")
@ -75,17 +49,4 @@ public class MemberExperienceLogController {
return success(MemberExperienceLogConvert.INSTANCE.convertPage(pageResult));
}
// TODO @疯狂导出可以先不支持场景不多
@GetMapping("/export-excel")
@Operation(summary = "导出会员经验记录 Excel")
@PreAuthorize("@ss.hasPermission('member:experience-log:export')")
@OperateLog(type = EXPORT)
public void exportExperienceLogExcel(@Valid MemberExperienceLogExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<MemberExperienceLogDO> list = experienceLogService.getExperienceLogList(exportReqVO);
// 导出 Excel
List<MemberExperienceLogExcelVO> datas = MemberExperienceLogConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "会员经验记录.xls", "数据", MemberExperienceLogExcelVO.class, datas);
}
}

View File

@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -62,16 +61,6 @@ public class MemberLevelController {
return success(MemberLevelConvert.INSTANCE.convert(level));
}
// TODO @疯狂这个应该用不到哈
@GetMapping("/list")
@Operation(summary = "获得会员等级列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('member:level:query')")
public CommonResult<List<MemberLevelRespVO>> getLevelList(@RequestParam("ids") Collection<Long> ids) {
List<MemberLevelDO> list = levelService.getLevelList(ids);
return success(MemberLevelConvert.INSTANCE.convertList(list));
}
@GetMapping("/list-all-simple")
@Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项")
public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleLevelList() {

View File

@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.member.controller.admin.level;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExcelVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogRespVO;
import cn.iocoder.yudao.module.member.convert.level.MemberLevelLogConvert;
@ -16,17 +12,15 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
// TODO @疯狂是不是不用这个 controller因为日志只是为了记录db 可以查询和审计即可目前暂时不需要开放出来
@Tag(name = "管理后台 - 会员等级记录")
@ -38,16 +32,6 @@ public class MemberLevelLogController {
@Resource
private MemberLevelLogService levelLogService;
// TODO @疯狂这个不允许删除哈
@DeleteMapping("/delete")
@Operation(summary = "删除会员等级记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('member:level-log:delete')")
public CommonResult<Boolean> deleteLevelLog(@RequestParam("id") Long id) {
levelLogService.deleteLevelLog(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会员等级记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@ -57,16 +41,6 @@ public class MemberLevelLogController {
return success(MemberLevelLogConvert.INSTANCE.convert(levelLog));
}
// TODO @疯狂这个接口应该没用
@GetMapping("/list")
@Operation(summary = "获得会员等级记录列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('member:level-log:query')")
public CommonResult<List<MemberLevelLogRespVO>> getLevelLogList(@RequestParam("ids") Collection<Long> ids) {
List<MemberLevelLogDO> list = levelLogService.getLevelLogList(ids);
return success(MemberLevelLogConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得会员等级记录分页")
@PreAuthorize("@ss.hasPermission('member:level-log:query')")
@ -74,18 +48,4 @@ public class MemberLevelLogController {
PageResult<MemberLevelLogDO> pageResult = levelLogService.getLevelLogPage(pageVO);
return success(MemberLevelLogConvert.INSTANCE.convertPage(pageResult));
}
// TODO @疯狂导出可以去掉先
@GetMapping("/export-excel")
@Operation(summary = "导出会员等级记录 Excel")
@PreAuthorize("@ss.hasPermission('member:level-log:export')")
@OperateLog(type = EXPORT)
public void exportLevelLogExcel(@Valid MemberLevelLogExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<MemberLevelLogDO> list = levelLogService.getLevelLogList(exportReqVO);
// 导出 Excel
List<MemberLevelLogExcelVO> datas = MemberLevelLogConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "会员等级记录.xls", "数据", MemberLevelLogExcelVO.class, datas);
}
}

View File

@ -8,8 +8,6 @@ import javax.validation.constraints.NotNull;
/**
* 会员经验记录 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*
* @author owen
*/
@Data
public class MemberExperienceLogBaseVO {

View File

@ -1,48 +0,0 @@
package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.member.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 会员经验记录 Excel VO
*
* @author owen
*/
@Data
public class MemberExperienceLogExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("用户编号")
private Long userId;
@ExcelProperty(value = "业务类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.MEMBER_EXPERIENCE_BIZ_TYPE)
private Integer bizType;
@ExcelProperty("业务编号")
private String bizId;
@ExcelProperty("标题")
private String title;
@ExcelProperty("经验")
private Integer experience;
@ExcelProperty("变更后的经验")
private Integer totalExperience;
@ExcelProperty("描述")
private String description;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员经验记录 Excel 导出 Request VO参数和 MemberExperienceLogPageReqVO 是一致的")
@Data
public class MemberExperienceLogExportReqVO {
@Schema(description = "用户编号", example = "3638")
private Long userId;
@Schema(description = "业务类型", example = "1")
private Integer bizType;
@Schema(description = "业务编号", example = "12164")
private String bizId;
@Schema(description = "标题", example = "增加经验")
private String title;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -11,9 +11,6 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员经验记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -7,9 +7,6 @@ import lombok.ToString;
import java.time.LocalDateTime;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员经验记录 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -14,8 +14,6 @@ import javax.validation.constraints.Positive;
/**
* 会员等级 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*
* @author owen
*/
@Data
public class MemberLevelBaseVO {

View File

@ -5,10 +5,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
// TODO @疯狂项目的 vo controller 不写 author 信息哈只写 swagger 注解
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -6,9 +6,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -7,9 +7,6 @@ import lombok.ToString;
import java.time.LocalDateTime;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.member.controller.admin.level.vo.level;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
// TODO @疯狂不需要继承 MemberLevelBaseVO
@Schema(description = "管理后台 - 会员等级 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberLevelSimpleRespVO extends MemberLevelBaseVO {
public class MemberLevelSimpleRespVO {
@Schema(description = "编号", example = "6103")
private Long id;

View File

@ -7,9 +7,6 @@ import lombok.ToString;
import javax.validation.constraints.NotNull;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -8,8 +8,6 @@ import javax.validation.constraints.NotNull;
/**
* 会员等级记录 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*
* @author owen
*/
@Data
public class MemberLevelLogBaseVO {

View File

@ -1,46 +0,0 @@
package cn.iocoder.yudao.module.member.controller.admin.level.vo.log;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 会员等级记录 Excel VO
*
* @author owen
*/
@Data
public class MemberLevelLogExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("用户编号")
private Long userId;
@ExcelProperty("等级编号")
private Long levelId;
@ExcelProperty("会员等级")
private Integer level;
@ExcelProperty("享受折扣")
private Integer discount;
@ExcelProperty("升级经验")
private Integer experience;
@ExcelProperty("会员此时的经验")
private Integer userExperience;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("描述")
private String description;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.member.controller.admin.level.vo.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级记录 Excel 导出 Request VO参数和 MemberLevelLogPageReqVO 是一致的")
@Data
public class MemberLevelLogExportReqVO {
@Schema(description = "用户编号", example = "25923")
private Long userId;
@Schema(description = "等级编号", example = "25985")
private Long levelId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -11,9 +11,6 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -7,9 +7,6 @@ import lombok.ToString;
import java.time.LocalDateTime;
/**
* @author owen
*/
@Schema(description = "管理后台 - 会员等级记录 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -7,9 +7,11 @@ import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReq
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import cn.iocoder.yudao.module.member.service.tag.MemberTagService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
@ -22,10 +24,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -42,6 +41,8 @@ public class MemberUserController {
private MemberTagService memberTagService;
@Resource
private MemberLevelService memberLevelService;
@Resource
private MemberGroupService memberGroupService;
@PutMapping("/update")
@Operation(summary = "更新会员用户")
@ -69,17 +70,27 @@ public class MemberUserController {
return success(PageResult.empty());
}
// 处理会员标签返显
Set<Long> groupIds = new HashSet<>(pageResult.getList().size());
// 处理用户标签返显
Set<Long> tagIds = pageResult.getList().stream()
.peek(m -> {
if (m.getGroupId() != null) {
groupIds.add(m.getGroupId());
}
})
.map(MemberUserDO::getTagIds)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
List<MemberTagDO> tags = memberTagService.getTagList(tagIds);
// 处理会员级别返显
// 处理用户级别返显
List<MemberLevelDO> levels = memberLevelService.getEnableLevelList();
// 拼接
return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels));
// 处理用户分组返显
List<MemberGroupDO> groups = memberGroupService.getGroupList(groupIds);
return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels, groups));
}
}

View File

@ -56,4 +56,7 @@ public class MemberUserBaseVO {
@Schema(description = "会员等级编号", example = "1")
private Long levelId;
@Schema(description = "用户分组编号", example = "1")
private Long groupId;
}

View File

@ -35,7 +35,10 @@ public class MemberUserPageReqVO extends PageParam {
@Schema(description = "会员标签编号列表", example = "[1, 2]")
private List<Long> tagIds;
@Schema(description = "会员等级", example = "1")
@Schema(description = "会员等级", example = "1")
private Long levelId;
@Schema(description = "用户分组编号", example = "1")
private Long groupId;
}

View File

@ -38,4 +38,7 @@ public class MemberUserRespVO extends MemberUserBaseVO {
@Schema(description = "会员等级", example = "黄金会员")
private String levelName;
@Schema(description = "用户分组", example = "购物达人")
private String groupName;
}

View File

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.member.convert.group;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupRespVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupSimpleRespVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 用户分组 Convert
*
* @author owen
*/
@Mapper
public interface MemberGroupConvert {
MemberGroupConvert INSTANCE = Mappers.getMapper(MemberGroupConvert.class);
MemberGroupDO convert(MemberGroupCreateReqVO bean);
MemberGroupDO convert(MemberGroupUpdateReqVO bean);
MemberGroupRespVO convert(MemberGroupDO bean);
List<MemberGroupRespVO> convertList(List<MemberGroupDO> list);
PageResult<MemberGroupRespVO> convertPage(PageResult<MemberGroupDO> page);
List<MemberGroupSimpleRespVO> convertSimpleList(List<MemberGroupDO> list);
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.member.convert.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExcelVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
import org.mapstruct.Mapper;
@ -25,6 +24,4 @@ public interface MemberExperienceLogConvert {
PageResult<MemberExperienceLogRespVO> convertPage(PageResult<MemberExperienceLogDO> page);
List<MemberExperienceLogExcelVO> convertList02(List<MemberExperienceLogDO> list);
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.member.convert.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExcelVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
import org.mapstruct.Mapper;
@ -25,6 +24,4 @@ public interface MemberLevelLogConvert {
PageResult<MemberLevelLogRespVO> convertPage(PageResult<MemberLevelLogDO> page);
List<MemberLevelLogExcelVO> convertList02(List<MemberLevelLogDO> list);
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserInfoRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
@ -38,15 +39,20 @@ public interface MemberUserConvert {
default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult,
List<MemberTagDO> tags,
List<MemberLevelDO> levels) {
List<MemberLevelDO> levels,
List<MemberGroupDO> groups) {
PageResult<MemberUserRespVO> result = convertPage(pageResult);
// 处理关联数据
Map<Long, String> tagMap = convertMap(tags, MemberTagDO::getId, MemberTagDO::getName);
Map<Long, String> levelMap = convertMap(levels, MemberLevelDO::getId, MemberLevelDO::getName);
Map<Long, String> groupMap = convertMap(groups, MemberGroupDO::getId, MemberGroupDO::getName);
// 填充关联数据
for (MemberUserRespVO vo : result.getList()) {
vo.setTagNames(convertList(vo.getTagIds(), tagMap::get));
vo.setLevelName(MapUtil.getStr(levelMap, vo.getLevelId(), StrUtil.EMPTY));
vo.setGroupName(MapUtil.getStr(groupMap, vo.getGroupId(), StrUtil.EMPTY));
}
return result;
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.member.dal.dataobject.group;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 用户分组 DO
*
* @author owen
*/
@TableName("member_group")
@KeySequence("member_group_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberGroupDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名称
*/
private String name;
/**
* 备注
*/
private String remark;
/**
* 状态
* <p>
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
}

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
@ -121,12 +122,18 @@ public class MemberUserDO extends TenantBaseDO {
/**
* 会员级别编号
*
* 关联 {@link MemberLevelDO#getLevel()} 字段
* 关联 {@link MemberLevelDO#getId()} 字段
*/
private Long levelId;
/**
* 会员经验
*/
private Integer experience;
/**
* 用户分组编号
*
* 关联 {@link MemberGroupDO#getId()} 字段
*/
private Long groupId;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.member.dal.mysql.group;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 用户分组 Mapper
*
* @author owen
*/
@Mapper
public interface MemberGroupMapper extends BaseMapperX<MemberGroupDO> {
default PageResult<MemberGroupDO> selectPage(MemberGroupPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<MemberGroupDO>()
.likeIfPresent(MemberGroupDO::getName, reqVO.getName())
.eqIfPresent(MemberGroupDO::getStatus, reqVO.getStatus())
.betweenIfPresent(MemberGroupDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(MemberGroupDO::getId));
}
default List<MemberGroupDO> selectListByStatus(Integer status) {
return selectList(MemberGroupDO::getStatus, status);
}
}

View File

@ -3,13 +3,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员经验记录 Mapper
*
@ -28,14 +25,4 @@ public interface MemberExperienceLogMapper extends BaseMapperX<MemberExperienceL
.orderByDesc(MemberExperienceLogDO::getId));
}
default List<MemberExperienceLogDO> selectList(MemberExperienceLogExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<MemberExperienceLogDO>()
.eqIfPresent(MemberExperienceLogDO::getUserId, reqVO.getUserId())
.eqIfPresent(MemberExperienceLogDO::getBizId, reqVO.getBizId())
.eqIfPresent(MemberExperienceLogDO::getBizType, reqVO.getBizType())
.eqIfPresent(MemberExperienceLogDO::getTitle, reqVO.getTitle())
.betweenIfPresent(MemberExperienceLogDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(MemberExperienceLogDO::getId));
}
}

View File

@ -3,13 +3,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员等级记录 Mapper
*
@ -26,12 +23,4 @@ public interface MemberLevelLogMapper extends BaseMapperX<MemberLevelLogDO> {
.orderByDesc(MemberLevelLogDO::getId));
}
default List<MemberLevelLogDO> selectList(MemberLevelLogExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<MemberLevelLogDO>()
.eqIfPresent(MemberLevelLogDO::getUserId, reqVO.getUserId())
.eqIfPresent(MemberLevelLogDO::getLevelId, reqVO.getLevelId())
.betweenIfPresent(MemberLevelLogDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(MemberLevelLogDO::getId));
}
}

View File

@ -45,6 +45,7 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
.betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
.eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
.apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql)
.orderByDesc(MemberUserDO::getId));
}
@ -62,4 +63,18 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.set(MemberUserDO::getExperience, 0)
.set(MemberUserDO::getLevelId, null));
}
default Long selectCountByGroupId(Long groupId) {
return selectCount(MemberUserDO::getGroupId, groupId);
}
default Long selectCountByLevelId(Long levelId) {
return selectCount(MemberUserDO::getLevelId, levelId);
}
default Long selectCountByTagId(Long tagId) {
return selectCount(new LambdaQueryWrapperX<MemberUserDO>()
.apply("FIND_IN_SET({0}, tag_ids)", tagId));
}
}

View File

@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.member.service.group;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
/**
* 用户分组 Service 接口
*
* @author owen
*/
public interface MemberGroupService {
/**
* 创建用户分组
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createGroup(@Valid MemberGroupCreateReqVO createReqVO);
/**
* 更新用户分组
*
* @param updateReqVO 更新信息
*/
void updateGroup(@Valid MemberGroupUpdateReqVO updateReqVO);
/**
* 删除用户分组
*
* @param id 编号
*/
void deleteGroup(Long id);
/**
* 获得用户分组
*
* @param id 编号
* @return 用户分组
*/
MemberGroupDO getGroup(Long id);
/**
* 获得用户分组列表
*
* @param ids 编号
* @return 用户分组列表
*/
List<MemberGroupDO> getGroupList(Collection<Long> ids);
/**
* 获得用户分组分页
*
* @param pageReqVO 分页查询
* @return 用户分组分页
*/
PageResult<MemberGroupDO> getGroupPage(MemberGroupPageReqVO pageReqVO);
/**
* 获得指定状态的用户分组列表
*
* @param status 状态
* @return 用户分组列表
*/
List<MemberGroupDO> getGroupListByStatus(Integer status);
/**
* 获得开启状态的用户分组列表
*
* @return 用户分组列表
*/
default List<MemberGroupDO> getEnableGroupList() {
return getGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
}
}

View File

@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.member.service.group;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
import cn.iocoder.yudao.module.member.convert.group.MemberGroupConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_HAS_USER;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_NOT_EXISTS;
/**
* 用户分组 Service 实现类
*
* @author owen
*/
@Service
@Validated
public class MemberGroupServiceImpl implements MemberGroupService {
@Resource
private MemberGroupMapper groupMapper;
@Resource
private MemberUserMapper memberUserMapper;
@Override
public Long createGroup(MemberGroupCreateReqVO createReqVO) {
// 插入
MemberGroupDO group = MemberGroupConvert.INSTANCE.convert(createReqVO);
groupMapper.insert(group);
// 返回
return group.getId();
}
@Override
public void updateGroup(MemberGroupUpdateReqVO updateReqVO) {
// 校验存在
validateGroupExists(updateReqVO.getId());
// 更新
MemberGroupDO updateObj = MemberGroupConvert.INSTANCE.convert(updateReqVO);
groupMapper.updateById(updateObj);
}
@Override
public void deleteGroup(Long id) {
// 校验存在
validateGroupExists(id);
// 校验分组下是否有用户
validateGroupHasUser(id);
// 删除
groupMapper.deleteById(id);
}
void validateGroupExists(Long id) {
if (groupMapper.selectById(id) == null) {
throw exception(GROUP_NOT_EXISTS);
}
}
void validateGroupHasUser(Long id) {
Long count = memberUserMapper.selectCountByGroupId(id);
if (count > 0) {
throw exception(GROUP_HAS_USER);
}
}
@Override
public MemberGroupDO getGroup(Long id) {
return groupMapper.selectById(id);
}
@Override
public List<MemberGroupDO> getGroupList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return groupMapper.selectBatchIds(ids);
}
@Override
public PageResult<MemberGroupDO> getGroupPage(MemberGroupPageReqVO pageReqVO) {
return groupMapper.selectPage(pageReqVO);
}
@Override
public List<MemberGroupDO> getGroupListByStatus(Integer status) {
return groupMapper.selectListByStatus(status);
}
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
@ -16,13 +15,6 @@ import java.util.List;
*/
public interface MemberExperienceLogService {
/**
* 删除会员经验记录
*
* @param id 编号
*/
void deleteExperienceLog(Long id);
/**
* 获得会员经验记录
*

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.member.service.level;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper;
@ -14,9 +13,6 @@ import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.EXPERIENCE_LOG_NOT_EXISTS;
/**
* 会员经验记录 Service 实现类
*
@ -29,19 +25,6 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
@Resource
private MemberExperienceLogMapper experienceLogMapper;
@Override
public void deleteExperienceLog(Long id) {
// 校验存在
validateExperienceLogExists(id);
// 删除
experienceLogMapper.deleteById(id);
}
private void validateExperienceLogExists(Long id) {
if (experienceLogMapper.selectById(id) == null) {
throw exception(EXPERIENCE_LOG_NOT_EXISTS);
}
}
@Override
public MemberExperienceLogDO getExperienceLog(Long id) {
@ -58,11 +41,6 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
return experienceLogMapper.selectPage(pageReqVO);
}
@Override
public List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO) {
return experienceLogMapper.selectList(exportReqVO);
}
@Override
public void createAdjustLog(Long userId, int experience, int totalExperience) {
// 管理员调整时, 没有业务编号, 记录对应的枚举值

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
@ -58,11 +57,6 @@ public class MemberLevelLogServiceImpl implements MemberLevelLogService {
return levelLogMapper.selectPage(pageReqVO);
}
@Override
public List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO) {
return levelLogMapper.selectList(exportReqVO);
}
@Override
public void createCancelLog(Long userId, String reason) {
MemberLevelLogDO levelLogDO = new MemberLevelLogDO();

View File

@ -74,9 +74,10 @@ public class MemberLevelServiceImpl implements MemberLevelService {
@Override
public void deleteLevel(Long id) {
// TODO @疯狂校验是否有用户使用该等级
// 校验存在
validateLevelExists(id);
// 校验分组下是否有用户
validateLevelHasUser(id);
// 删除
levelMapper.deleteById(id);
}
@ -148,6 +149,14 @@ public class MemberLevelServiceImpl implements MemberLevelService {
validateExperienceOutRange(list, id, level, experience);
}
@VisibleForTesting
void validateLevelHasUser(Long id) {
Long count = memberUserMapper.selectCountByLevelId(id);
if (count > 0) {
throw exception(GROUP_HAS_USER);
}
}
@Override
public MemberLevelDO getLevel(Long id) {
return levelMapper.selectById(id);

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReq
import cn.iocoder.yudao.module.member.convert.tag.MemberTagConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
import cn.iocoder.yudao.module.member.dal.mysql.tag.MemberTagMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -18,8 +19,7 @@ import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.TAG_NAME_EXISTS;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.TAG_NOT_EXISTS;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
/**
* 会员标签 Service 实现类
@ -32,6 +32,8 @@ public class MemberTagServiceImpl implements MemberTagService {
@Resource
private MemberTagMapper tagMapper;
@Resource
private MemberUserMapper memberUserMapper;
@Override
public Long createTag(MemberTagCreateReqVO createReqVO) {
@ -59,6 +61,8 @@ public class MemberTagServiceImpl implements MemberTagService {
public void deleteTag(Long id) {
// 校验存在
validateTagExists(id);
// 校验标签下是否有用户
validateTagHasUser(id);
// 删除
tagMapper.deleteById(id);
}
@ -87,6 +91,13 @@ public class MemberTagServiceImpl implements MemberTagService {
}
}
void validateTagHasUser(Long id) {
Long count = memberUserMapper.selectCountByTagId(id);
if (count > 0) {
throw exception(TAG_HAS_USER);
}
}
@Override
public MemberTagDO getTag(Long id) {
return tagMapper.selectById(id);

View File

@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.member.service.group;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_HAS_USER;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link MemberGroupServiceImpl} 的单元测试类
*
* @author owen
*/
@Import(MemberGroupServiceImpl.class)
public class MemberGroupServiceImplTest extends BaseDbUnitTest {
@Resource
private MemberGroupServiceImpl groupService;
@Resource
private MemberGroupMapper groupMapper;
@Resource
private MemberUserMapper memberUserMapper;
@Test
public void testCreateGroup_success() {
// 准备参数
MemberGroupCreateReqVO reqVO = randomPojo(MemberGroupCreateReqVO.class);
// 调用
Long groupId = groupService.createGroup(reqVO);
// 断言
assertNotNull(groupId);
// 校验记录的属性是否正确
MemberGroupDO group = groupMapper.selectById(groupId);
assertPojoEquals(reqVO, group);
}
@Test
public void testUpdateGroup_success() {
// mock 数据
MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
// 准备参数
MemberGroupUpdateReqVO reqVO = randomPojo(MemberGroupUpdateReqVO.class, o -> {
o.setId(dbGroup.getId()); // 设置更新的 ID
});
// 调用
groupService.updateGroup(reqVO);
// 校验是否更新正确
MemberGroupDO group = groupMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, group);
}
@Test
public void testUpdateGroup_notExists() {
// 准备参数
MemberGroupUpdateReqVO reqVO = randomPojo(MemberGroupUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> groupService.updateGroup(reqVO), GROUP_NOT_EXISTS);
}
@Test
public void testDeleteGroup_success() {
// mock 数据
MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbGroup.getId();
// 调用
groupService.deleteGroup(id);
// 校验数据不存在了
assertNull(groupMapper.selectById(id));
}
@Test
public void testDeleteGroup_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> groupService.deleteGroup(id), GROUP_NOT_EXISTS);
}
@Test
public void testDeleteGroup_hasUser() {
// mock 数据
MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbGroup.getId();
// mock 会员数据
MemberUserDO dbUser = randomPojo(MemberUserDO.class, o -> {
o.setGroupId(id);
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
});
memberUserMapper.insert(dbUser);
// 调用, 并断言异常
assertServiceException(() -> groupService.deleteGroup(id), GROUP_HAS_USER);
}
@Test
public void testGetGroupPage() {
String name = randomString();
int status = CommonStatusEnum.ENABLE.getStatus();
// mock 数据
MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class, o -> { // 等会查询到
o.setName(name);
o.setStatus(status);
o.setCreateTime(buildTime(2023, 2, 18));
});
groupMapper.insert(dbGroup);
// 测试 name 不匹配
groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setName("")));
// 测试 status 不匹配
groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setCreateTime(null)));
// 准备参数
MemberGroupPageReqVO reqVO = new MemberGroupPageReqVO();
reqVO.setName(name);
reqVO.setStatus(status);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<MemberGroupDO> pageResult = groupService.getGroupPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbGroup, pageResult.getList().get(0));
}
}

View File

@ -1,4 +1,5 @@
DELETE FROM "member_user";
DELETE FROM "member_address";
DELETE FROM "member_tag";
DELETE FROM "member_level";
DELETE FROM "member_level";
DELETE FROM "member_group";

View File

@ -1,19 +1,30 @@
CREATE TABLE IF NOT EXISTS "member_user" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
"avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
"status" tinyint NOT NULL COMMENT '状态',
"mobile" varchar(11) NOT NULL COMMENT '手机号',
"password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
CREATE TABLE IF NOT EXISTS "member_user"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
"name" varchar(30) NULL COMMENT '真实名字',
sex tinyint null comment '性别',
birthday datetime null comment '出生日期',
area_id int null comment '所在地',
mark varchar(255) null comment '用户备注',
point int default 0 null comment '积分',
"avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
"status" tinyint NOT NULL COMMENT '状态',
"mobile" varchar(11) NOT NULL COMMENT '手机号',
"password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
"register_ip" varchar(32) NOT NULL COMMENT '注册 IP',
"login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
"login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
"creator" varchar(64) NULL DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) NULL DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
"tenant_id" bigint not null default '0',
"login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
"login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
"tag_ids" varchar(255) NULL DEFAULT NULL COMMENT '用户标签编号列表,以逗号分隔',
"level_id" bigint NULL DEFAULT NULL COMMENT '等级编号',
"experience" bigint NULL DEFAULT NULL COMMENT '经验',
"group_id" bigint NULL DEFAULT NULL COMMENT '用户分组编号',
"creator" varchar(64) NULL DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) NULL DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '会员表';
@ -63,4 +74,19 @@ CREATE TABLE IF NOT EXISTS "member_level"
"tenant_id" bigint not null default '0',
"status" int NOT NULL,
PRIMARY KEY ("id")
) COMMENT '会员等级';
) COMMENT '会员等级';
CREATE TABLE IF NOT EXISTS "member_group"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"remark" varchar NOT NULL,
"status" varchar NOT NULL,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '用户分组';