diff --git a/DB/version4.2.5toMASTER.sql b/DB/version4.2.5toMASTER.sql index d0fa6793..8646223f 100644 --- a/DB/version4.2.5toMASTER.sql +++ b/DB/version4.2.5toMASTER.sql @@ -6,4 +6,4 @@ CREATE TABLE `li_member_coupon_sign` ( `invalid_time` datetime NULL DEFAULT NULL COMMENT '过期时间', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC; \ No newline at end of file +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;git \ No newline at end of file diff --git a/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java b/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java index e5de7c6b..951e56ec 100644 --- a/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java +++ b/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java @@ -1,14 +1,17 @@ package cn.lili.controller.member; import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.context.UserContext; import cn.lili.common.vo.PageVO; import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.member.service.FootprintService; import cn.lili.modules.search.entity.dos.EsGoodsIndex; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; +import org.apache.catalina.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -35,8 +38,9 @@ public class FootprintController { @ApiOperation(value = "分页获取") @GetMapping - public ResultMessage> getByPage(PageVO page) { - return ResultUtil.data(footprintService.footPrintPage(page)); + public ResultMessage> getByPage(FootPrintQueryParams params) { + params.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(footprintService.footPrintPage(params)); } @ApiOperation(value = "根据id删除") diff --git a/buyer-api/src/main/java/cn/lili/controller/order/CartController.java b/buyer-api/src/main/java/cn/lili/controller/order/CartController.java index f78e4d78..005a1c0c 100644 --- a/buyer-api/src/main/java/cn/lili/controller/order/CartController.java +++ b/buyer-api/src/main/java/cn/lili/controller/order/CartController.java @@ -189,20 +189,38 @@ public class CartController { } } + @ApiOperation(value = "选择自提地址") + @ApiImplicitParams({ + @ApiImplicitParam(name = "storeAddressId", value = "自提地址id ", required = true, paramType = "query"), + @ApiImplicitParam(name = "way", value = "购物车类型 ", paramType = "query") + }) + @GetMapping("/storeAddress") + public ResultMessage shippingSelfPickAddress(@NotNull(message = "自提地址ID不能为空") String storeAddressId, + String way) { + try { + cartService.shippingSelfAddress(storeAddressId, way); + return ResultUtil.success(); + } catch (ServiceException se) { + log.error(ResultCode.SHIPPING_NOT_APPLY.message(), se); + throw new ServiceException(ResultCode.SHIPPING_NOT_APPLY); + } catch (Exception e) { + log.error(ResultCode.CART_ERROR.message(), e); + throw new ServiceException(ResultCode.CART_ERROR); + } + } + @ApiOperation(value = "选择配送方式") @ApiImplicitParams({ @ApiImplicitParam(name = "shippingMethod", value = "配送方式:SELF_PICK_UP(自提)," + "LOCAL_TOWN_DELIVERY(同城配送)," + "LOGISTICS(物流) ", required = true, paramType = "query"), - @ApiImplicitParam(name = "selleId", value = "店铺id", paramType = "query"), @ApiImplicitParam(name = "way", value = "购物车类型 ", paramType = "query") }) - @GetMapping("/shippingMethod") + @PutMapping("/shippingMethod") public ResultMessage shippingMethod(@NotNull(message = "配送方式不能为空") String shippingMethod, - String selleId, String way) { try { - cartService.shippingMethod(selleId, shippingMethod, way); + cartService.shippingMethod( shippingMethod, way); return ResultUtil.success(); } catch (ServiceException se) { log.error(se.getMsg(), se); @@ -213,6 +231,21 @@ public class CartController { } } + @ApiOperation(value = "获取用户可选择的物流方式") + @ApiImplicitParams({ + @ApiImplicitParam(name = "way", value = "购物车类型 ", paramType = "query") + }) + @GetMapping("/shippingMethodList") + public ResultMessage shippingMethodList(String way) { + try { + return ResultUtil.data(cartService.shippingMethodList(way)); + } + catch (Exception e) { + e.printStackTrace(); + return ResultUtil.error(ResultCode.ERROR); + } + } + @ApiOperation(value = "选择发票") @ApiImplicitParams({ @ApiImplicitParam(name = "way", value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT ", required = true, paramType = "query"), diff --git a/buyer-api/src/main/java/cn/lili/controller/store/StoreAddressBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/store/StoreAddressBuyerController.java new file mode 100644 index 00000000..a51acdf4 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/store/StoreAddressBuyerController.java @@ -0,0 +1,53 @@ +package cn.lili.controller.store; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.OperationalJudgment; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.dos.StoreAddress; +import cn.lili.modules.store.service.StoreAddressService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +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 java.util.Objects; + +/** + * 买家端,商家地址(自提点)接口 + * + * @author chc + * @since 2022/6/2114:46 + */ +@RestController +@Api(tags = "买家端,商家地址(自提点)接口") +@RequestMapping("/buyer/store/address") +public class StoreAddressBuyerController { + + /** + * 店铺自提点 + */ + @Autowired + private StoreAddressService storeAddressService; + + @ApiOperation(value = "获取商家自提点分页") + @ApiImplicitParam(name = "storeId", value = "店铺Id", required = true, dataType = "String", paramType = "path") + @GetMapping("/page/{storeId}") + public ResultMessage> get(PageVO pageVo,@PathVariable String storeId) { + return ResultUtil.data(storeAddressService.getStoreAddress(storeId, pageVo)); + } + + @ApiOperation(value = "获取商家自提点信息") + @ApiImplicitParam(name = "id", value = "自提点ID", required = true, paramType = "path") + @GetMapping("/{id}") + public ResultMessage get(@PathVariable String id) { + StoreAddress address = OperationalJudgment.judgment(storeAddressService.getById(id)); + return ResultUtil.data(address); + } +} diff --git a/buyer-api/src/main/resources/application.yml b/buyer-api/src/main/resources/application.yml index f3a7a5a6..04c87cf4 100644 --- a/buyer-api/src/main/resources/application.yml +++ b/buyer-api/src/main/resources/application.yml @@ -265,6 +265,8 @@ lili: order-group: lili_order_group member-topic: lili_member_topic member-group: lili_member_group + store-topic: lili_store_topic + store-group: lili_store_group other-topic: lili_other_topic other-group: lili_other_group notice-topic: lili_notice_topic diff --git a/consumer/src/main/java/cn/lili/event/MemberInfoChangeEvent.java b/consumer/src/main/java/cn/lili/event/MemberInfoChangeEvent.java new file mode 100644 index 00000000..51dbd391 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/MemberInfoChangeEvent.java @@ -0,0 +1,17 @@ +package cn.lili.event; + +import cn.lili.modules.member.entity.dos.Member; + +/** + * @author chc + * @since 2022/6/2114:46 + */ +public interface MemberInfoChangeEvent { + + /** + * 会员信息更改消息 + * + * @param member 会员信息 + */ + void memberInfoChange(Member member); +} diff --git a/consumer/src/main/java/cn/lili/event/StoreSettingChangeEvent.java b/consumer/src/main/java/cn/lili/event/StoreSettingChangeEvent.java new file mode 100644 index 00000000..53fb83b6 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/StoreSettingChangeEvent.java @@ -0,0 +1,18 @@ +package cn.lili.event; + + +import cn.lili.modules.store.entity.dos.Store; + +/** + * @author chc + * @since 2022/6/2114:46 + */ +public interface StoreSettingChangeEvent { + + /** + * 店铺信息更改消息 + * + * @param store 店铺信息 + */ + void storeSettingChange(Store store); +} diff --git a/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java b/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java index 7b9f138a..4edd7f41 100644 --- a/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java +++ b/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java @@ -44,8 +44,9 @@ public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDa public void orderChange(OrderMessage orderMessage) { switch (orderMessage.getNewStatus()) { - //订单带校验/订单代发货,则记录分销信息 + //订单带校验/订单代发货/待自提,则记录分销信息 case TAKE: + case STAY_PICKED_UP: case UNDELIVERED: { //记录分销订单 distributionOrderService.calculationDistribution(orderMessage.getOrderSn()); diff --git a/consumer/src/main/java/cn/lili/event/impl/ImTalkExecute.java b/consumer/src/main/java/cn/lili/event/impl/ImTalkExecute.java new file mode 100644 index 00000000..b631589f --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/ImTalkExecute.java @@ -0,0 +1,60 @@ +package cn.lili.event.impl; + +import cn.lili.event.MemberInfoChangeEvent; +import cn.lili.event.StoreSettingChangeEvent; +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.modules.im.service.ImTalkService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.store.entity.dos.Store; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * Im对话消息 + * + * @author chc + * @since 2022/6/2114:46 + */ +@Service +public class ImTalkExecute implements MemberInfoChangeEvent, StoreSettingChangeEvent { + + @Autowired + private ImTalkService imTalkService; + + @Override + public void memberInfoChange(Member member) { + //当与UserId1相等时 + List imTalkList1 = imTalkService.list(new LambdaQueryWrapper().eq(ImTalk::getUserId1, member.getId())); + for (ImTalk imTalk : imTalkList1) { + imTalk.setName1(member.getNickName()); + imTalk.setFace1(member.getFace()); + } + imTalkService.updateBatchById(imTalkList1); + List imTalkList2 = imTalkService.list(new LambdaQueryWrapper().eq(ImTalk::getUserId2, member.getId())); + for (ImTalk imTalk : imTalkList2) { + imTalk.setName2(member.getNickName()); + imTalk.setFace2(member.getFace()); + } + imTalkService.updateBatchById(imTalkList2); + } + + @Override + public void storeSettingChange(Store store) { + //当与UserId1相等时 + List imTalkList1 = imTalkService.list(new LambdaQueryWrapper().eq(ImTalk::getUserId1, store.getId())); + for (ImTalk imTalk : imTalkList1) { + imTalk.setName1(store.getStoreName()); + imTalk.setFace1(store.getStoreLogo()); + } + imTalkService.updateBatchById(imTalkList1); + List imTalkList2 = imTalkService.list(new LambdaQueryWrapper().eq(ImTalk::getUserId2, store.getId())); + for (ImTalk imTalk : imTalkList2) { + imTalk.setName2(store.getStoreName()); + imTalk.setFace2(store.getStoreLogo()); + } + imTalkService.updateBatchById(imTalkList2); + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/VerificationOrderExecute.java b/consumer/src/main/java/cn/lili/event/impl/VerificationOrderExecute.java index 008a2fc2..a0b50055 100644 --- a/consumer/src/main/java/cn/lili/event/impl/VerificationOrderExecute.java +++ b/consumer/src/main/java/cn/lili/event/impl/VerificationOrderExecute.java @@ -31,7 +31,7 @@ public class VerificationOrderExecute implements OrderStatusChangeEvent { @Override public void orderChange(OrderMessage orderMessage) { //订单状态为待核验,添加订单添加核验码 - if (orderMessage.getNewStatus().equals(OrderStatusEnum.TAKE)) { + if (orderMessage.getNewStatus().equals(OrderStatusEnum.TAKE) || orderMessage.getNewStatus().equals(OrderStatusEnum.STAY_PICKED_UP)) { //获取订单信息 Order order = orderService.getBySn(orderMessage.getOrderSn()); //获取随机数,判定是否存在 diff --git a/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java b/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java index cbab13e4..58fd2e27 100644 --- a/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java +++ b/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java @@ -42,6 +42,7 @@ public class WechatMessageExecute implements OrderStatusChangeEvent, TradeEvent case PAID: case UNDELIVERED: case DELIVERED: + case STAY_PICKED_UP: case COMPLETED: try { wechatMessageUtil.sendWechatMessage(orderMessage.getOrderSn()); diff --git a/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java b/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java index ab767f10..2e384744 100644 --- a/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java +++ b/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java @@ -1,10 +1,8 @@ package cn.lili.listener; import cn.hutool.json.JSONUtil; -import cn.lili.event.MemberLoginEvent; -import cn.lili.event.MemberPointChangeEvent; -import cn.lili.event.MemberRegisterEvent; -import cn.lili.event.MemberWithdrawalEvent; +import cn.lili.event.*; +import cn.lili.event.impl.ImTalkExecute; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.entity.dos.MemberSign; import cn.lili.modules.member.entity.dto.MemberPointMessage; @@ -58,6 +56,9 @@ public class MemberMessageListener implements RocketMQListener { @Autowired private List memberLoginEvents; + @Autowired + private List memberInfoChangeEvents; + @Override public void onMessage(MessageExt messageExt) { @@ -110,6 +111,20 @@ public class MemberMessageListener implements RocketMQListener { } } break; + //会员信息更改 + case MEMBER_INFO_EDIT: + for (MemberInfoChangeEvent memberInfoChangeEvent : memberInfoChangeEvents) { + try { + Member member = JSONUtil.toBean(new String(messageExt.getBody()), Member.class); + memberInfoChangeEvent.memberInfoChange(member); + } catch (Exception e) { + log.error("会员{},在{}业务中,提现事件执行异常", + new String(messageExt.getBody()), + memberInfoChangeEvent.getClass().getName(), + e); + } + } + break; //会员提现 case MEMBER_WITHDRAWAL: for (MemberWithdrawalEvent memberWithdrawalEvent : memberWithdrawalEvents) { diff --git a/consumer/src/main/java/cn/lili/listener/StoreMessageListener.java b/consumer/src/main/java/cn/lili/listener/StoreMessageListener.java new file mode 100644 index 00000000..243de85f --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/StoreMessageListener.java @@ -0,0 +1,51 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.event.MemberRegisterEvent; +import cn.lili.event.StoreSettingChangeEvent; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.rocketmq.tags.StoreTagsEnum; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 店铺消息 + * @author chc + * @since 2022/6/2114:46 + */ +@Component +@Slf4j +@RocketMQMessageListener(topic = "${lili.data.rocketmq.store-topic}", consumerGroup = "${lili.data.rocketmq.store-group}") +public class StoreMessageListener implements RocketMQListener { + @Autowired + private List storeSettingChangeEventList; + + @Override + public void onMessage(MessageExt messageExt) { + switch (StoreTagsEnum.valueOf(messageExt.getTags())){ + //修改店铺 + case EDIT_STORE_SETTING: + for (StoreSettingChangeEvent storeSettingChangeEvent : storeSettingChangeEventList) { + try { + Store store = JSONUtil.toBean(new String(messageExt.getBody()), Store.class); + storeSettingChangeEvent.storeSettingChange(store); + } catch (Exception e) { + log.error("会员{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + storeSettingChangeEvent.getClass().getName(), + e); + } + } + break; + default: + break; + } + } +} diff --git a/consumer/src/main/resources/application.yml b/consumer/src/main/resources/application.yml index ae49a9b8..f3c3ed20 100644 --- a/consumer/src/main/resources/application.yml +++ b/consumer/src/main/resources/application.yml @@ -260,6 +260,8 @@ lili: order-group: lili_order_group member-topic: lili_member_topic member-group: lili_member_group + store-topic: lili_store_topic + store-group: lili_store_group other-topic: lili_other_topic other-group: lili_other_group notice-topic: lili_notice_topic diff --git a/framework/pom.xml b/framework/pom.xml index da681cb3..5e0b4b74 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -51,7 +51,10 @@ - + + org.springframework.boot + spring-boot-starter-websocket + org.springframework.boot spring-boot-starter-web diff --git a/framework/src/main/java/cn/lili/common/security/AuthUser.java b/framework/src/main/java/cn/lili/common/security/AuthUser.java index 95978ce3..819ae6e6 100644 --- a/framework/src/main/java/cn/lili/common/security/AuthUser.java +++ b/framework/src/main/java/cn/lili/common/security/AuthUser.java @@ -70,6 +70,12 @@ public class AuthUser implements Serializable { */ private Boolean isSuper = false; + /** + * 租户id + */ + private String tenantId; + + public AuthUser(String username, String id, String nickName, String face, UserEnums role) { this.username = username; this.face = face; diff --git a/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java b/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java index 07d86b64..919b6693 100644 --- a/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java +++ b/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java @@ -14,7 +14,8 @@ public enum UserEnums { MEMBER("会员"), STORE("商家"), MANAGER("管理员"), - SYSTEM("系统"); + SYSTEM("系统"), + SEAT("坐席"); private final String role; UserEnums(String role) { diff --git a/framework/src/main/java/cn/lili/common/swagger/Swagger2Config.java b/framework/src/main/java/cn/lili/common/swagger/Swagger2Config.java index e604498b..3b795d22 100644 --- a/framework/src/main/java/cn/lili/common/swagger/Swagger2Config.java +++ b/framework/src/main/java/cn/lili/common/swagger/Swagger2Config.java @@ -81,6 +81,32 @@ public class Swagger2Config { .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } + @Bean + public Docket orderRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("订单") + .apiInfo(apiInfo()).select() + //扫描所有有注解的api,用这种方式更灵活 +// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.order")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket imRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("im") + .apiInfo(apiInfo()).select() + //扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.im")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } @Bean public Docket memberRestApi() { diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java index 84236cda..50eee35a 100644 --- a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java @@ -327,7 +327,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl i //记录用户足迹 if (UserContext.getCurrentUser() != null) { - FootPrint footPrint = new FootPrint(UserContext.getCurrentUser().getId(), goodsId, skuId); + FootPrint footPrint = new FootPrint(UserContext.getCurrentUser().getId(), goodsIndex.getStoreId(), goodsId, skuId); String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.VIEW_GOODS.name(); rocketMQTemplate.asyncSend(destination, footPrint, RocketmqSendCallbackBuilder.commonCallback()); } diff --git a/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java b/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java new file mode 100644 index 00000000..d9f825c0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java @@ -0,0 +1,34 @@ +package cn.lili.modules.im.config; + + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import javax.websocket.server.ServerEndpointConfig; + +/** + * CustomSpringConfigurator + * + * @author Chopper + * @version v1.0 + * 2021-12-31 11:53 + */ +public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware { + + /** + * Spring application context. + */ + private static volatile BeanFactory context; + + @Override + public T getEndpointInstance(Class clazz) throws InstantiationException { + return context.getBean(clazz); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + CustomSpringConfigurator.context = applicationContext; + } +} diff --git a/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java new file mode 100644 index 00000000..c6de471b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java @@ -0,0 +1,24 @@ +package cn.lili.modules.im.config; + + +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * WebSocketConfigurator + * + * @author Chopper + * @version v1.0 + * 2021-12-31 11:53 + */ +@ConditionalOnWebApplication +@Configuration +public class WebSocketConfigurator { + + @Bean + public CustomSpringConfigurator customSpringConfigurator() { + // This is just to get context + return new CustomSpringConfigurator(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java new file mode 100644 index 00000000..f381faca --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java @@ -0,0 +1,69 @@ +package cn.lili.modules.im.entity.dos; + +import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.im.entity.enums.MessageTypeEnum; +import cn.lili.modules.im.entity.vo.MessageOperation; +import cn.lili.mybatis.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @author Chopper + */ +@Data +@TableName("li_im_message") +@ApiModel(value = "Im消息") +@NoArgsConstructor +@AllArgsConstructor +public class ImMessage extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 发送者 + */ + private String fromUser; + + /** + * 接收者 + */ + private String toUser; + + /** + * 已阅 + */ + private Boolean isRead; + + /** + * 消息类型 + */ + private MessageTypeEnum messageType; + + /** + * 聊天id + */ + private String talkId; + + /** + * 消息实体 + */ + private String text; + + + public ImMessage(MessageOperation messageOperation){ + this.setFromUser(messageOperation.getFrom()); + this.setMessageType(messageOperation.getMessageType()); + this.setIsRead(false); + this.setText(messageOperation.getContext()); + this.setTalkId(messageOperation.getTalkId()); + this.setCreateTime(new Date()); + this.setToUser(messageOperation.getTo()); + this.setId(SnowFlake.getIdStr()); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java new file mode 100644 index 00000000..56ffe258 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java @@ -0,0 +1,102 @@ +package cn.lili.modules.im.entity.dos; + + +import cn.lili.common.utils.SnowFlake; +import cn.lili.mybatis.BaseTenantEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @author Chopper + */ +@Data +@TableName("li_im_talk") +@ApiModel(value = "聊天") +@NoArgsConstructor +@AllArgsConstructor +public class ImTalk extends BaseTenantEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("用户1 id") + private String userId1; + + @ApiModelProperty("用户2 id") + private String userId2; + + @ApiModelProperty("用户1置顶") + private Boolean top1; + + @ApiModelProperty("用户2置顶") + private Boolean top2; + + @ApiModelProperty("用户1 不可见") + private Boolean disable1; + + @ApiModelProperty("用户2 不可见") + private Boolean disable2; + + @ApiModelProperty("用户1名字") + private String name1; + + @ApiModelProperty("用户2名字") + private String name2; + + @ApiModelProperty("用户1头像") + private String face1; + + @ApiModelProperty("用户2头像") + private String face2; + + @ApiModelProperty("用户1的店铺标识") + private Boolean storeFlag1; + + @ApiModelProperty("用户2的店铺标识") + private Boolean storeFlag2; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "最后聊天时间", hidden = true) + private Date lastTalkTime; + + @ApiModelProperty(value = "最后聊天内容") + private String lastTalkMessage; + + @ApiModelProperty(value = "最后发送消息类型") + private String lastMessageType; + + @ApiModelProperty(value = "坐席Id") + private String tenantId; + + @ApiModelProperty(value = "坐席名称") + private String tenantName; + + + public ImTalk(String userId1, String userId2, + String face1, String face2, + String name1, String name2 + ) { + this.userId1 = userId1; + this.userId2 = userId2; + this.top1 = false; + this.top2 = false; + this.disable1 = false; + this.disable2 = false; + this.storeFlag1 = false; + this.storeFlag2 = false; + this.setId(SnowFlake.getIdStr()); + this.lastTalkTime = new Date(); + this.face1 = face1; + this.face2 = face2; + this.name1 = name1; + this.name2 = name2; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/QA.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/QA.java new file mode 100644 index 00000000..0d6bd3f2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/QA.java @@ -0,0 +1,31 @@ +package cn.lili.modules.im.entity.dos; + +import cn.lili.mybatis.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 问题答案 + * + * @author Chopper + * @version v1.0 + * 2022-02-09 17:59 + */ +@Data +@TableName("li_qa") +@ApiModel(value = "租户问答") +@NoArgsConstructor +public class QA extends BaseEntity { + + @ApiModelProperty(value = "租户id") + private Integer tenantId; + + @ApiModelProperty(value = "问题") + private String question; + + @ApiModelProperty(value = "答案") + private String answer; +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/Seat.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/Seat.java new file mode 100644 index 00000000..40bdfd84 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/Seat.java @@ -0,0 +1,47 @@ +package cn.lili.modules.im.entity.dos; + +import cn.lili.mybatis.BaseTenantEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; + +/** + * 坐席 + * + * @author Chopper + * @version v1.0 + * 2022-02-09 17:08 + */ +@Data +@TableName("li_seat") +@ApiModel(value = "坐席") +@NoArgsConstructor +public class Seat extends BaseTenantEntity { + + @ApiModelProperty(value = "租户id") + private String tenantId; + + @ApiModelProperty(value = "坐席用户名") + private String username; + + @ApiModelProperty(value = "会员头像") + private String face; + + @ApiModelProperty(value = "坐席密码") + private String password; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "坐席状态") + private Boolean disabled; + + @NotEmpty(message = "手机号码不能为空") + @ApiModelProperty(value = "手机号码", required = true) + private String mobile; + +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/SeatSetting.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/SeatSetting.java new file mode 100644 index 00000000..a462ce7e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/SeatSetting.java @@ -0,0 +1,36 @@ +package cn.lili.modules.im.entity.dos; + +import cn.lili.mybatis.BaseTenantEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 坐席设置 + * + * @author Chopper + * @version v1.0 + * 2022-02-09 17:55 + */ +@Data +@TableName("li_seat_setting") +@ApiModel(value = "坐席设置") +@NoArgsConstructor +public class SeatSetting extends BaseTenantEntity { + + @ApiModelProperty(value = "租户idid") + private String tenantId; + + @ApiModelProperty(value = "欢迎语") + private String welcome; + + @ApiModelProperty(value = "离线自动回复") + private String outLineAutoReply; + + @ApiModelProperty(value = "长时间自动回复") + private String longTermAutoReply; + + +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dto/ImQueryParams.java b/framework/src/main/java/cn/lili/modules/im/entity/dto/ImQueryParams.java new file mode 100644 index 00000000..8dc1f0fa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dto/ImQueryParams.java @@ -0,0 +1,36 @@ +package cn.lili.modules.im.entity.dto; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author chc + * @since 2022/6/2114:46 + */ +@Data +@ApiModel +public class ImQueryParams extends PageVO { + + @ApiModelProperty("用户Id") + private String memberId; + + @ApiModelProperty("店铺Id") + private String storeId; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(memberId)) { + queryWrapper.eq("member_id", memberId); + } + if (CharSequenceUtil.isNotEmpty(storeId)) { + queryWrapper.eq("store_id", storeId); + } + queryWrapper.eq("delete_flag",false); + queryWrapper.orderByDesc("create_time"); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java b/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java new file mode 100644 index 00000000..372522c1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java @@ -0,0 +1,50 @@ +package cn.lili.modules.im.entity.dto; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.im.entity.dos.ImMessage; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.Data; + +/** + * MessageQueryParams + * + * @author Chopper + * @version v1.0 + * 2022-01-20 17:16 + */ +@Data +public class MessageQueryParams extends PageVO { + /** + * 聊天窗口 + */ + private String talkId; + /** + * 最后一个消息 + */ + private String lastMessageId; + /** + * 获取消息数量 + */ + private Integer num; + + public LambdaQueryWrapper initQueryWrapper() { + if (StringUtils.isEmpty(talkId)) { + throw new ServiceException(ResultCode.ERROR); + } + if (num == null || num > 50) { + num = 50; + } + + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(ImMessage::getTalkId, talkId); + if (StringUtils.isNotEmpty(lastMessageId)) { + lambdaQueryWrapper.lt(ImMessage::getId, lastMessageId); + } + lambdaQueryWrapper.orderByDesc(ImMessage::getCreateTime); +// lambdaQueryWrapper.last("limit " + num); + return lambdaQueryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageResultType.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageResultType.java new file mode 100644 index 00000000..ba54b5d5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageResultType.java @@ -0,0 +1,28 @@ +package cn.lili.modules.im.entity.enums; + +/** + * 返回消息类型枚举 + * + * @author liushuai + */ +public enum MessageResultType { + /** + * 返回消息类型枚举 + *

+ * 好友列表 + * 增加好友 + * 消息 + * 阅读消息 + * 未读消息 + * 历史消息 + * 系统提示 + */ + FRIENDS, + ADD_FRIENDS, + MESSAGE, + READ_MESSAGE, + UN_READ, + HISTORY, + SYSTEM_TIPS + +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java new file mode 100644 index 00000000..687c814c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java @@ -0,0 +1,31 @@ +package cn.lili.modules.im.entity.enums; + +/** + * 消息的类型 + * + * @author liushuai(liushuai711 @ gmail.com) + * @version v4.0 + * @Description: + * @since 2022/2/10 16:36 + */ +public enum MessageStatusEnum { + //socket刚打开时发送的消息,这个一般是是刚打开socket链接,进行登录,传入token用 + CONNECT, + //心跳类型的消息,此种类型的消息只有 type 、 text 两种属性 + HEARTBEAT, + //用户打开一个对话框,准备跟某人聊天时 + OPEN, + //客服进行自动回复。客户端发起这种类型请求,则是在拉取对方是否有自动回复,如果有,服务端就会给客户端发送过自动回复的信息 + AUTO_REPLY, + //正常收发消息沟通,文字、表情等沟通 + MSG, + //扩展。比如发送商品、发送订单 + EXTEND, + //系统提示,如提示 对方已离线 + SYSTEM, + //服务端发送到客户端,用于设置客户端的用户信息。会吧 com.xnx3.yunkefu.core.vo.bean.User 传过去 + SET_USER, + //结束服务 + CLOSE_SERVICE; + +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java new file mode 100644 index 00000000..6b06d3b7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java @@ -0,0 +1,23 @@ +package cn.lili.modules.im.entity.enums; + +/** + * 消息类型 + * + * @author liushuai + */ +public enum MessageTypeEnum { + /** + * 消息类型枚举 + *

+ * 普通消息 + * 图片 + * 语音 + * 视频 + */ + MESSAGE, + PICTURE, + VOICE, + GOODS, + ORDER, + VIDEO +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/OnlineStatusEnum.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/OnlineStatusEnum.java new file mode 100644 index 00000000..837a4493 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/OnlineStatusEnum.java @@ -0,0 +1,14 @@ +package cn.lili.modules.im.entity.enums; + +/** + * 坐席在线状态 + * + * @author Chopper + * @version v1.0 + * 2022-02-10 16:37 + */ +public enum OnlineStatusEnum { + // 在线/下线 + ONLINE, + OUTLINE; +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java new file mode 100644 index 00000000..5909222e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java @@ -0,0 +1,26 @@ +package cn.lili.modules.im.entity.enums; + +/** + * 操作类型枚举 + * + * @author liushuai + */ +public enum OperationType { + /** + * 消息类型枚举 + *

+ * 心跳检测 + * 发起聊天 + * 发起消息 + * 查询历史消息 + * 阅读消息 + * 查询未读消息 + */ + PING, + CREATE, + MESSAGE, + HISTORY, + READ, + UNREAD, + +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java new file mode 100644 index 00000000..6d80a8be --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java @@ -0,0 +1,82 @@ +package cn.lili.modules.im.entity.vo; + +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.mybatis.BaseTenantEntity; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @author Chopper + */ +@Data +@ApiModel(value = "聊天") +public class ImTalkVO extends BaseTenantEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("id") + private String id; + + @ApiModelProperty("用户 id") + private String userId; + + @ApiModelProperty("置顶") + private Boolean top; + + @ApiModelProperty("用户 不可见") + private Boolean disable; + + @ApiModelProperty("用户名字") + private String name; + + @ApiModelProperty("用户头像") + private String face; + + @ApiModelProperty("店铺标识") + private Boolean storeFlag; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "最后聊天时间", hidden = true) + private Date lastTalkTime; + + @ApiModelProperty(value = "最后聊天内容") + private String lastTalkMessage; + + @ApiModelProperty(value = "最后发送消息类型") + private String lastMessageType; + + @ApiModelProperty(value = "未读数量") + private Long unread; + + public ImTalkVO() { + + } + + public ImTalkVO(ImTalk imTalk, String currentUser) { + if (imTalk.getUserId2().equals(currentUser)) { + userId = imTalk.getUserId1(); + top = imTalk.getTop1(); + disable = imTalk.getDisable1(); + name = imTalk.getName1(); + face = imTalk.getFace1(); + storeFlag = imTalk.getStoreFlag1(); + } else { + userId = imTalk.getUserId2(); + top = imTalk.getTop2(); + disable = imTalk.getDisable2(); + name = imTalk.getName2(); + face = imTalk.getFace2(); + storeFlag = imTalk.getStoreFlag2(); + } + lastTalkMessage = imTalk.getLastTalkMessage(); + lastTalkTime = imTalk.getLastTalkTime(); + lastMessageType = imTalk.getLastMessageType(); + id = imTalk.getId(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java new file mode 100644 index 00000000..b4360bc6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java @@ -0,0 +1,53 @@ +package cn.lili.modules.im.entity.vo; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.im.entity.enums.MessageTypeEnum; +import cn.lili.modules.im.entity.enums.OperationType; +import lombok.Data; + +/** + * @author liushuai + */ +@Data +public class MessageOperation { + + /** + * 消息类型 + */ + private OperationType operationType; + /** + * 与某人聊天记录 + */ + private String to; + + /** + * 发送者 + */ + private String from; + + /** + * 聊天id + */ + private String talkId; + + /** + * 消息类型 + */ + private MessageTypeEnum messageType; + /** + * 消息内容 + */ + private String context; + + public void setOperationType(String operationType) { + if (!StringUtils.isEmpty(operationType)) { + this.operationType = OperationType.valueOf(operationType); + } + } + + public void setMessageType(String messageType) { + if (!StringUtils.isEmpty(messageType)) { + this.messageType = MessageTypeEnum.valueOf(messageType); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageVO.java new file mode 100644 index 00000000..420091e1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.im.entity.vo; + +import cn.lili.modules.im.entity.enums.MessageResultType; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * MessageVO + * + * @author Chopper + * @version v1.0 + * 2021-12-30 15:51 + */ +@Data +@AllArgsConstructor +public class MessageVO { + + /** + * 消息类型 + */ + private MessageResultType messageResultType; + /** + * 消息内容 + */ + private Object result; +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ReadMessage.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ReadMessage.java new file mode 100644 index 00000000..bbeb7d11 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ReadMessage.java @@ -0,0 +1,17 @@ +package cn.lili.modules.im.entity.vo; + +import lombok.Data; + +import java.util.List; + +/** + * ReadMessage + * + * @author Chopper + * @version v1.0 + * 2021-12-31 11:13 + */ +@Data +public class ReadMessage { + private List readMessageList; +} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/SeatVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/SeatVO.java new file mode 100644 index 00000000..d1df9d1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/SeatVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.im.entity.vo; + +import cn.lili.modules.im.entity.dos.Seat; +import lombok.Data; + +/** + * 客服VO + * + * @author Chopper + * @version v1.0 + * 2022-02-10 15:02 + */ +@Data +public class SeatVO extends Seat { + + /** + * 在线状态 + */ + private String onlineStatus; + + +} diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/ImMessageMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/ImMessageMapper.java new file mode 100644 index 00000000..e82b0cac --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/mapper/ImMessageMapper.java @@ -0,0 +1,12 @@ +package cn.lili.modules.im.mapper; + +import cn.lili.modules.im.entity.dos.ImMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * Im消息 Dao层 + * @author Chopper + */ +public interface ImMessageMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/ImTalkMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/ImTalkMapper.java new file mode 100644 index 00000000..d5f0d2b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/mapper/ImTalkMapper.java @@ -0,0 +1,12 @@ +package cn.lili.modules.im.mapper; + +import cn.lili.modules.im.entity.dos.ImTalk; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 聊天 Dao层 + * @author Chopper + */ +public interface ImTalkMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/QAMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/QAMapper.java new file mode 100644 index 00000000..f5592316 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/mapper/QAMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.im.mapper; + + +import cn.lili.modules.im.entity.dos.QA; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 问答处理层 + * + * @author pikachu + * @since 2020-02-18 15:18:56 + */ +public interface QAMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/SeatMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/SeatMapper.java new file mode 100644 index 00000000..98d7b6ae --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/mapper/SeatMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.im.mapper; + + +import cn.lili.modules.im.entity.dos.Seat; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 坐席处理层 + * + * @author pikachu + * @since 2020-02-18 15:18:56 + */ +public interface SeatMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/SeatSettingMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/SeatSettingMapper.java new file mode 100644 index 00000000..9e130ef3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/mapper/SeatSettingMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.im.mapper; + + +import cn.lili.modules.im.entity.dos.SeatSetting; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 坐席设置处理层 + * + * @author pikachu + * @since 2020-02-18 15:18:56 + */ +public interface SeatSettingMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java new file mode 100644 index 00000000..57fee7cc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java @@ -0,0 +1,63 @@ +package cn.lili.modules.im.service; + +import cn.lili.modules.im.entity.dos.ImMessage; +import cn.lili.modules.im.entity.dto.MessageQueryParams; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * Im消息 业务层 + * + * @author Chopper + */ +public interface ImMessageService extends IService { + + /** + * 阅读消息 + * + * @param talkId + * @param accessToken + */ + void read(String talkId, String accessToken); + + /** + * 未读消息列表 + * + * @param accessToken + */ + List unReadMessages(String accessToken); + + /** + * 历史消息 + * + * @param accessToken + * @param to + */ + List historyMessage(String accessToken, String to); + + /** + * 是否有新消息 + * @param accessToken + * @return + */ + Boolean hasNewMessage(String accessToken); + + /** + * 分页获取消息列表 + * @param messageQueryParams 查询条件 + * @return 消息列表 + */ + List getList(MessageQueryParams messageQueryParams); + + /** + * 获取所有未读消息 + * @return + */ + Long unreadMessageCount(); + + /** + * 清空所有未读消息 + */ + void cleanUnreadMessage(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java b/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java new file mode 100644 index 00000000..427750fd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java @@ -0,0 +1,51 @@ +package cn.lili.modules.im.service; + +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.modules.im.entity.vo.ImTalkVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 聊天 业务层 + * + * @author Chopper + */ +public interface ImTalkService extends IService { + + /** + * 获取与某人的聊天框 + * + * @param userId1 + * @param userId2 + * @return + */ + ImTalk getTalkByUser(String userId1, String userId2); + + /** + * 置顶消息 + * + * @param id + * @param top + */ + void top(String id, Boolean top); + + /** + * 禁用(前端不做展示)聊天 + * + * @param id + */ + void disable(String id); + + /** + * 获取用户聊天列表 + * @return + */ + List getUserTalkList(String userName); + + /** + * 获取商家聊天列表 + * @return + */ + List getStoreTalkList(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/QAService.java b/framework/src/main/java/cn/lili/modules/im/service/QAService.java new file mode 100644 index 00000000..49f8572b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/QAService.java @@ -0,0 +1,25 @@ +package cn.lili.modules.im.service; + + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.im.entity.dos.QA; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 问答 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +public interface QAService extends IService { + + /** + * 查询店铺问题 + * @param word + * @param pageVO + * @return + */ + IPage getStoreQA(String word, PageVO pageVO); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/SeatService.java b/framework/src/main/java/cn/lili/modules/im/service/SeatService.java new file mode 100644 index 00000000..b0cf8eac --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/SeatService.java @@ -0,0 +1,60 @@ +package cn.lili.modules.im.service; + + +import cn.lili.common.security.token.Token; +import cn.lili.modules.im.entity.dos.Seat; +import cn.lili.modules.im.entity.vo.SeatVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 坐席业务 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +public interface SeatService extends IService { + + + /** + * 获取坐席列表 + * + * @param storeId 店铺id + * @return + */ + List seatVoList(String storeId); + + /** + * 坐席登录 + * + * @param username + * @param password + * @return + */ + Token usernameLogin(String username, String password); + + /** + * 快捷登录code 生成 + * + * @param username 用户名 + * @return + */ + String createQuickLoginCode(String username); + + /** + * 快捷登录 + * + * @param code + * @return + */ + Token quickLogin(String code); + + /** + * 查询坐席 + * + * @param username + * @return + */ + Seat findByUsername(String username); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/SeatSettingService.java b/framework/src/main/java/cn/lili/modules/im/service/SeatSettingService.java new file mode 100644 index 00000000..364ba10f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/SeatSettingService.java @@ -0,0 +1,30 @@ +package cn.lili.modules.im.service; + +import cn.lili.modules.im.entity.dos.SeatSetting; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 坐席设置业务 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +public interface SeatSettingService extends IService { + + + /** + * 根据店铺id获取坐席配置 + * + * @param storeId + * @return + */ + SeatSetting getSetting(String storeId); + + /** + * 根据店铺修改坐席设置 + * + * @param seatSetting 坐席设置 + * @return + */ + SeatSetting updateByStore(SeatSetting seatSetting); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java new file mode 100644 index 00000000..c216c282 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java @@ -0,0 +1,143 @@ +package cn.lili.modules.im.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.modules.im.entity.dos.ImMessage; +import cn.lili.modules.im.entity.dto.MessageQueryParams; +import cn.lili.modules.im.mapper.ImMessageMapper; +import cn.lili.modules.im.service.ImMessageService; +import cn.lili.modules.im.service.ImTalkService; +import cn.lili.mybatis.util.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * Im消息 业务实现 + * + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ImMessageServiceImpl extends ServiceImpl implements ImMessageService { + + @Autowired + private ImTalkService imTalkService; + + @Override + public void read(String talkId, String accessToken) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + String userId = UserContext.getAuthUser(accessToken).getId(); + updateWrapper.eq(ImMessage::getTalkId, talkId); + updateWrapper.eq(ImMessage::getToUser, userId); + updateWrapper.set(ImMessage::getIsRead, true); + this.update(updateWrapper); + } + + @Override + public List unReadMessages(String accessToken) { + String userId = UserContext.getAuthUser(accessToken).getId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ImMessage::getToUser, userId); + queryWrapper.eq(ImMessage::getIsRead, false); + return this.list(queryWrapper); + } + + @Override + public List historyMessage(String accessToken, String to) { + String userId = UserContext.getAuthUser(accessToken).getId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.and(i -> i.eq(ImMessage::getToUser, userId).and(j -> j.eq(ImMessage::getFromUser, to))); + queryWrapper.or(i -> i.eq(ImMessage::getToUser, to).and(j -> j.eq(ImMessage::getFromUser, userId))); + queryWrapper.orderByDesc(ImMessage::getCreateTime); + return this.list(queryWrapper); + } + + @Override + public Boolean hasNewMessage(String accessToken) { + String userId = UserContext.getAuthUser(accessToken).getId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ImMessage::getIsRead, false); + queryWrapper.eq(ImMessage::getToUser, userId); + return this.list(queryWrapper).size() > 0; + + } + + @Override + public List getList(MessageQueryParams messageQueryParams) { + List messageList = this.page(PageUtil.initPage(messageQueryParams), messageQueryParams.initQueryWrapper()).getRecords(); + ListSort(messageList); + readMessage(messageList); + return messageList; + } + + @Override + public Long unreadMessageCount() { + AuthUser currentUser = UserContext.getCurrentUser(); + if(currentUser == null){ + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + return this.count(new LambdaQueryWrapper().eq(ImMessage::getToUser,currentUser.getId()).eq(ImMessage::getIsRead,false)); + } + + @Override + public void cleanUnreadMessage() { + AuthUser currentUser = UserContext.getCurrentUser(); + if(currentUser == null){ + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + this.update(new LambdaUpdateWrapper().eq(ImMessage::getToUser,currentUser.getId()).set(ImMessage::getIsRead,true)); + } + + /** + * 根据时间倒叙 + * + * @param list + */ + private static void ListSort(List list) { + list.sort(new Comparator() { + @Override + public int compare(ImMessage e1, ImMessage e2) { + try { + if (e1.getCreateTime().before(e2.getCreateTime())) { + return -1; + } else { + return 1; + } + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + }); + } + + + /** + * 阅读消息 + * + * @param messageList 消息列表 + */ + private void readMessage(List messageList) { + if (messageList.size() > 0) { + for (ImMessage imMessage : messageList) { + if(Boolean.FALSE.equals(imMessage.getIsRead())){ + imMessage.setIsRead(true); + } + } + } + this.updateBatchById(messageList); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java new file mode 100644 index 00000000..2b24c8bb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java @@ -0,0 +1,187 @@ +package cn.lili.modules.im.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.modules.im.entity.dos.ImMessage; +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.modules.im.entity.vo.ImTalkVO; +import cn.lili.modules.im.mapper.ImTalkMapper; +import cn.lili.modules.im.service.ImMessageService; +import cn.lili.modules.im.service.ImTalkService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.stringtemplate.v4.ST; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 聊天 业务实现 + * + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ImTalkServiceImpl extends ServiceImpl implements ImTalkService { + + @Autowired + private MemberService memberService; + + @Autowired + private StoreService storeService; + + @Autowired + private ImMessageService imMessageService; + + @Override + public ImTalk getTalkByUser(String userId1, String userId2) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ImTalk::getUserId2, userId2); + queryWrapper.eq(ImTalk::getUserId1, userId1); + ImTalk imTalk = this.getOne(queryWrapper); + AuthUser currentUser = UserContext.getCurrentUser(); + //如果没有聊天,则创建聊天 + if (imTalk == null) { + // 没有登录的这个账户信息 + if (currentUser == null) { + return null; + } + //当自己为店铺时 + if(UserEnums.STORE.equals(currentUser.getRole())){ + Store selfStore = storeService.getById(userId1); + //没有这个用户信息 + Member other = memberService.getById(userId2); + if(other == null){ + return null; + } + //自己为店铺其他人必定为用户 + imTalk = new ImTalk(userId1, userId2, selfStore.getStoreLogo(), other.getFace(), selfStore.getStoreName(), other.getNickName()); + imTalk.setStoreFlag1(true); + }else if(UserEnums.MEMBER.equals(currentUser.getRole())){ + //没有这个店铺信息 + Member self = memberService.getById(userId1); + Member otherMember = memberService.getById(userId2); + Store otherStore = storeService.getById(userId2); + if(otherStore != null){ + imTalk = new ImTalk(userId1, userId2, self.getFace(), otherStore.getStoreLogo(), self.getNickName(), otherStore.getStoreName()); + imTalk.setStoreFlag2(true); + }else if (otherMember != null){ + imTalk = new ImTalk(userId1, userId2, self.getFace(), otherMember.getFace(), self.getNickName(), otherMember.getNickName()); + }else{ + return null; + } + } + this.save(imTalk); + } else { + imTalk = check(imTalk); + } + return imTalk; + } + + /** + * 发起聊天后,如果聊天不可见为true,则需要修正 + * + * @param imTalk + */ + private ImTalk check(ImTalk imTalk) { + if (imTalk.getDisable1() || imTalk.getDisable2()) { + imTalk.setDisable1(false); + imTalk.setDisable2(false); + this.updateById(imTalk); + + } + return imTalk; + } + + @Override + public void top(String id, Boolean top) { + ImTalk imTalk = this.getById(id); + if (imTalk.getUserId1().equals(UserContext.getCurrentUser().getId())) { + imTalk.setTop1(top); + } else if (imTalk.getUserId2().equals(UserContext.getCurrentUser().getId())) { + imTalk.setTop2(top); + } else { + throw new ServiceException(ResultCode.ERROR); + } + this.updateById(imTalk); + } + + @Override + public void disable(String id) { + ImTalk imTalk = this.getById(id); + if (imTalk.getUserId1().equals(UserContext.getCurrentUser().getId())) { + imTalk.setDisable1(true); + this.updateById(imTalk); + } else if (imTalk.getUserId2().equals(UserContext.getCurrentUser().getId())) { + imTalk.setDisable2(true); + this.updateById(imTalk); + } + } + + @Override + public List getUserTalkList(String userName) { + AuthUser authUser = UserContext.getCurrentUser(); + if(authUser == null){ + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.and(wq->{ + wq.like(ImTalk::getName1, userName).or().like(ImTalk::getName2,userName); + }); + queryWrapper.and(wq->{ + wq.like(ImTalk::getUserId1, authUser.getId()).or().like(ImTalk::getUserId2,authUser.getId()); + }); + queryWrapper.orderByDesc(ImTalk::getLastTalkTime); + List imTalks = this.list(queryWrapper); + List imTalkVOList = imTalks.stream().map(imTalk -> { + return new ImTalkVO(imTalk, authUser.getId()); + }).collect(Collectors.toList()); + getUnread(imTalkVOList); + return imTalkVOList; + } + + @Override + public List getStoreTalkList() { + AuthUser authUser = UserContext.getCurrentUser(); + if(authUser == null){ + throw new ServiceException(ResultCode.STORE_NOT_LOGIN_ERROR); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.and(wq->{ + wq.like(ImTalk::getUserId1, authUser.getStoreId()).or().like(ImTalk::getUserId2,authUser.getStoreId()); + }); + queryWrapper.orderByDesc(ImTalk::getLastTalkTime); + List imTalks = this.list(queryWrapper); + + List imTalkVOList = imTalks.stream().map(imTalk -> { + return new ImTalkVO(imTalk, authUser.getStoreId()); + }).collect(Collectors.toList()); + getUnread(imTalkVOList); + return imTalkVOList; + } + + /** + * 获取未读消息数量 + * @param imTalkVOList + */ + private void getUnread(List imTalkVOList){ + if(imTalkVOList.size() > 0){ + for (ImTalkVO imTalkVO : imTalkVOList) { + long count = imMessageService.count(new LambdaQueryWrapper().eq(ImMessage::getFromUser, imTalkVO.getUserId()).eq(ImMessage::getIsRead, false)); + imTalkVO.setUnread(count); + } + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/QAServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/QAServiceImpl.java new file mode 100644 index 00000000..2ee78fbc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/QAServiceImpl.java @@ -0,0 +1,32 @@ +package cn.lili.modules.im.serviceimpl; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.im.entity.dos.QA; +import cn.lili.modules.im.mapper.QAMapper; +import cn.lili.modules.im.service.QAService; +import cn.lili.mybatis.util.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 坐席业务层实现 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class QAServiceImpl extends ServiceImpl implements QAService { + + @Override + public IPage getStoreQA(String word, PageVO pageVo) { + LambdaQueryWrapper qaLambdaQueryWrapper = new LambdaQueryWrapper<>(); + qaLambdaQueryWrapper.eq(QA::getTenantId, UserContext.getCurrentUser().getTenantId()); + qaLambdaQueryWrapper.like(QA::getQuestion, word); + return this.page(PageUtil.initPage(pageVo), qaLambdaQueryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatServiceImpl.java new file mode 100644 index 00000000..cac28af1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatServiceImpl.java @@ -0,0 +1,111 @@ +package cn.lili.modules.im.serviceimpl; + +import cn.lili.cache.Cache; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.token.Token; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.im.entity.dos.Seat; +import cn.lili.modules.im.entity.enums.OnlineStatusEnum; +import cn.lili.modules.im.entity.vo.SeatVO; +import cn.lili.modules.im.mapper.SeatMapper; +import cn.lili.modules.im.service.SeatService; +import cn.lili.modules.im.token.SeatTokenGenerate; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * 坐席业务层实现 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class SeatServiceImpl extends ServiceImpl implements SeatService { + + + @Autowired + private SeatTokenGenerate seatTokenGenerate; + + @Autowired + private Cache cache; + + /** + * 快捷登录缓存前缀 + */ + private static String prefix = "{quick_login}_"; + + + @Override + public List seatVoList(String storeId) { + + LambdaQueryWrapper seatLambdaQueryWrapper = new LambdaQueryWrapper<>(); + seatLambdaQueryWrapper.eq(Seat::getTenantId, storeId); + List list = this.list(seatLambdaQueryWrapper); + + //转换模型为VO + List results = list.stream().map(item -> (SeatVO) item).collect(Collectors.toList()); + //填充坐席当前状态 + //todo + results.forEach(item -> { + item.setOnlineStatus(OnlineStatusEnum.ONLINE.name()); + }); + return results; + } + + @Override + public Token usernameLogin(String username, String password) { + + Seat seat = this.findByUsername(username); + //判断用户是否存在 + if (seat == null || !seat.getDisabled()) { + throw new ServiceException(ResultCode.ERROR); + } + //判断密码是否输入正确 + if (!new BCryptPasswordEncoder().matches(password, seat.getPassword())) { + throw new ServiceException(ResultCode.ERROR); + } + return seatTokenGenerate.createToken(seat, true); + } + + @Override + public String createQuickLoginCode(String username) { + String code = UUID.randomUUID().toString(); + cache.put(prefix + code, username, 20L); + return code; + } + + @Override + public Token quickLogin(String code) { + String username = cache.get(prefix + code); + cache.remove(prefix + code); + if (StringUtils.isEmpty(username)) { + throw new ServiceException(ResultCode.ERROR); + } + return seatTokenGenerate.createToken(findByUsername(username), true); + } + + /** + * 查询坐席 + * + * @param username + * @return + */ + @Override + public Seat findByUsername(String username) { + LambdaQueryWrapper seatLambdaQueryWrapper = new LambdaQueryWrapper<>(); + seatLambdaQueryWrapper.eq(Seat::getUsername, username); + return this.getOne(seatLambdaQueryWrapper); + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatSettingServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatSettingServiceImpl.java new file mode 100644 index 00000000..e5ec4a8e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/SeatSettingServiceImpl.java @@ -0,0 +1,48 @@ +package cn.lili.modules.im.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.im.entity.dos.SeatSetting; +import cn.lili.modules.im.mapper.SeatSettingMapper; +import cn.lili.modules.im.service.SeatSettingService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 坐席设置业务层实现 + * + * @author pikachu + * @since 2020-02-18 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class SeatSettingServiceImpl extends ServiceImpl implements SeatSettingService { + @Override + public SeatSetting getSetting(String storeId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SeatSetting::getTenantId, storeId); + SeatSetting seatSetting = this.baseMapper.selectOne(queryWrapper); + if (seatSetting == null) { + seatSetting = new SeatSetting(); + seatSetting.setOutLineAutoReply("您好,我现在不在线,请您留下关键内容和联系方式,我看到后会立马回电。"); + seatSetting.setLongTermAutoReply("您好,我正在查阅相关资料,请您稍等。"); + seatSetting.setWelcome("您好,请问有什么可以帮您?"); + seatSetting.setTenantId(storeId); + this.save(seatSetting); + } + return seatSetting; + } + + @Override + public SeatSetting updateByStore(SeatSetting seatSetting) { + SeatSetting oldSetting = this.baseMapper.selectById(seatSetting.getId()); + if (oldSetting.getTenantId().equals(seatSetting.getTenantId())) { + this.updateById(seatSetting); + } else { + throw new ServiceException(ResultCode.ERROR); + } + return seatSetting; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java b/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java new file mode 100644 index 00000000..482bcded --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java @@ -0,0 +1,42 @@ +package cn.lili.modules.im.token; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.security.token.Token; +import cn.lili.common.security.token.TokenUtil; +import cn.lili.common.security.token.base.AbstractTokenGenerate; +import cn.lili.modules.im.entity.dos.Seat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 会员token生成 + * + * @author Chopper + * @version v4.0 + * @since 2020/11/16 10:50 + */ +@Component +public class SeatTokenGenerate extends AbstractTokenGenerate { + @Autowired + private TokenUtil tokenUtil; + + @Override + public Token createToken(Seat seat, Boolean longTerm) { + AuthUser authUser = new AuthUser( + seat.getUsername(), + seat.getId(), + seat.getNickName(), + seat.getFace(), + UserEnums.SEAT); + authUser.setTenantId(seat.getTenantId()); + //登陆成功生成token + return tokenUtil.createToken(seat.getUsername(), authUser, longTerm, UserEnums.SEAT); + } + + @Override + public Token refreshToken(String refreshToken) { + return tokenUtil.refreshToken(refreshToken, UserEnums.SEAT); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java index 7b42ea3f..ed32fb2f 100644 --- a/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java @@ -28,6 +28,9 @@ public class FootPrint extends BaseEntity { @ApiModelProperty(value = "会员ID") private String memberId; + @ApiModelProperty(value = "店铺Id") + private String storeId; + @ApiModelProperty(value = "商品ID") private String goodsId; diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/FootPrintQueryParams.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/FootPrintQueryParams.java new file mode 100644 index 00000000..d2a5ec50 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/FootPrintQueryParams.java @@ -0,0 +1,36 @@ +package cn.lili.modules.member.entity.dto; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author chc + * @since 2022/6/2114:46 + */ +@Data +@ApiModel +public class FootPrintQueryParams extends PageVO { + + @ApiModelProperty("用户Id") + private String memberId; + + @ApiModelProperty("店铺Id") + private String storeId; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(memberId)) { + queryWrapper.eq("member_id", memberId); + } + if (CharSequenceUtil.isNotEmpty(storeId)) { + queryWrapper.eq("store_id", storeId); + } + queryWrapper.eq("delete_flag",false); + queryWrapper.orderByDesc("create_time"); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java index 3005ce96..a3ff318e 100644 --- a/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java +++ b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java @@ -2,6 +2,7 @@ package cn.lili.modules.member.service; import cn.lili.common.vo.PageVO; import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.search.entity.dos.EsGoodsIndex; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; @@ -42,10 +43,10 @@ public interface FootprintService extends IService { /** * 获取会员浏览历史分页 * - * @param pageVO 分页 + * @param params 分页 * @return 会员浏览历史列表 */ - IPage footPrintPage(PageVO pageVO); + IPage footPrintPage(FootPrintQueryParams params); /** * 获取当前会员的浏览记录数量 @@ -53,4 +54,5 @@ public interface FootprintService extends IService { * @return 当前会员的浏览记录数量 */ long getFootprintNum(); + } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java index 025c16bc..1b1d8be8 100644 --- a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java @@ -252,7 +252,6 @@ public interface MemberService extends IService { void logout(UserEnums userEnums); /** - * <<<<<<< HEAD * 修改会员是否拥有店铺 * * @param haveStore 是否拥有店铺 diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java index cfbbc554..f0c92f6c 100644 --- a/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java @@ -3,6 +3,7 @@ package cn.lili.modules.member.serviceimpl; import cn.lili.common.security.context.UserContext; import cn.lili.common.vo.PageVO; import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.member.mapper.FootprintMapper; import cn.lili.modules.member.service.FootprintService; import cn.lili.modules.search.entity.dos.EsGoodsIndex; @@ -43,19 +44,17 @@ public class FootprintServiceImpl extends ServiceImpl oldPrints = list(queryWrapper); if (oldPrints != null && !oldPrints.isEmpty()) { FootPrint oldPrint = oldPrints.get(0); - oldPrint.setSkuId(footPrint.getSkuId()); - this.updateById(oldPrint); - return oldPrint; - } else { - footPrint.setCreateTime(new Date()); - this.save(footPrint); - //删除超过100条后的记录 - this.baseMapper.deleteLastFootPrint(footPrint.getMemberId()); - return footPrint; + this.removeById(oldPrint.getId()); } + footPrint.setCreateTime(new Date()); + this.save(footPrint); + //删除超过100条后的记录 + this.baseMapper.deleteLastFootPrint(footPrint.getMemberId()); + return footPrint; } @Override @@ -74,15 +73,8 @@ public class FootprintServiceImpl extends ServiceImpl footPrintPage(PageVO pageVO) { - - LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); - lambdaQueryWrapper.eq(FootPrint::getMemberId, UserContext.getCurrentUser().getId()); - lambdaQueryWrapper.eq(FootPrint::getDeleteFlag, false); - lambdaQueryWrapper.orderByDesc(FootPrint::getCreateTime); - IPage footPrintPages = this.page(PageUtil.initPage(pageVO), lambdaQueryWrapper); - - + public IPage footPrintPage(FootPrintQueryParams params) { + IPage footPrintPages = this.page(PageUtil.initPage(params), params.queryWrapper()); //定义结果 IPage esGoodsIndexIPage = new Page<>(); @@ -90,7 +82,7 @@ public class FootprintServiceImpl extends ServiceImpl list = esGoodsSearchService.getEsGoodsBySkuIds( - footPrintPages.getRecords().stream().map(FootPrint::getSkuId).collect(Collectors.toList()), pageVO); + footPrintPages.getRecords().stream().map(FootPrint::getSkuId).collect(Collectors.toList()), params); esGoodsIndexIPage.setPages(footPrintPages.getPages()); esGoodsIndexIPage.setRecords(list); diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java index 30585133..a35c8433 100644 --- a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java @@ -40,13 +40,16 @@ import cn.lili.modules.store.entity.dos.Store; import cn.lili.modules.store.entity.enums.StoreStatusEnum; import cn.lili.modules.store.service.StoreService; import cn.lili.mybatis.util.PageUtil; +import cn.lili.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.rocketmq.tags.MemberTagsEnum; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -94,6 +97,9 @@ public class MemberServiceImpl extends ServiceImpl impleme @Autowired private RocketmqCustomProperties rocketmqCustomProperties; + @Autowired + private RocketMQTemplate rocketMQTemplate; + @Autowired private ApplicationEventPublisher applicationEventPublisher; /** @@ -183,7 +189,8 @@ public class MemberServiceImpl extends ServiceImpl impleme @Override public Token usernameStoreLogin(String username, String password) { - Member member = this.findMember(username); +// Member member = this.findMember(username); + Member member = this.getOne(new LambdaQueryWrapper().eq(Member::getMobile,username)); //判断用户是否存在 if (member == null || !member.getDisabled()) { throw new ServiceException(ResultCode.USER_NOT_EXIST); @@ -301,6 +308,9 @@ public class MemberServiceImpl extends ServiceImpl impleme BeanUtil.copyProperties(memberEditDTO, member); //修改会员 this.updateById(member); + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_INFO_EDIT.name(); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, member, RocketmqSendCallbackBuilder.commonCallback()); return member; } diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java index 45d9cd87..d522286e 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java @@ -11,6 +11,7 @@ import cn.lili.modules.order.order.entity.vo.OrderVO; import cn.lili.modules.order.order.entity.vo.ReceiptVO; import cn.lili.modules.promotion.entity.dos.MemberCoupon; import cn.lili.modules.promotion.entity.vos.MemberCouponVO; +import cn.lili.modules.store.entity.dos.StoreAddress; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -106,6 +107,10 @@ public class TradeDTO implements Serializable { */ private MemberAddress memberAddress; + /** + * 自提地址 + */ + private StoreAddress storeAddress; /** * 客户端类型 diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java index d00b1bec..e04782a4 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java @@ -160,35 +160,37 @@ public class CheckDataRender implements CartRenderStep { private void groupStore(TradeDTO tradeDTO) { //渲染的购物车 List cartList = new ArrayList<>(); - - //根据店铺分组 - Map> storeCollect = tradeDTO.getSkuList().stream().collect(Collectors.groupingBy(CartSkuVO::getStoreId)); - for (Map.Entry> storeCart : storeCollect.entrySet()) { - if (!storeCart.getValue().isEmpty()) { - CartVO cartVO = new CartVO(storeCart.getValue().get(0)); - if (CharSequenceUtil.isEmpty(cartVO.getDeliveryMethod())) { - cartVO.setDeliveryMethod(DeliveryMethodEnum.LOGISTICS.name()); - } - cartVO.setSkuList(storeCart.getValue()); - try { - //筛选属于当前店铺的优惠券 - storeCart.getValue().forEach(i -> i.getPromotionMap().forEach((key, value) -> { - if (key.contains(PromotionTypeEnum.COUPON.name())) { - JSONObject promotionsObj = JSONUtil.parseObj(value); - Coupon coupon = JSONUtil.toBean(promotionsObj, Coupon.class); - if (key.contains(PromotionTypeEnum.COUPON.name()) && coupon.getStoreId().equals(storeCart.getKey())) { - cartVO.getCanReceiveCoupon().add(new CouponVO(coupon)); + if(tradeDTO.getCartList() == null || tradeDTO.getCartList().size() == 0){ + //根据店铺分组 + Map> storeCollect = tradeDTO.getSkuList().stream().collect(Collectors.groupingBy(CartSkuVO::getStoreId)); + for (Map.Entry> storeCart : storeCollect.entrySet()) { + if (!storeCart.getValue().isEmpty()) { + CartVO cartVO = new CartVO(storeCart.getValue().get(0)); + if (CharSequenceUtil.isEmpty(cartVO.getDeliveryMethod())) { + cartVO.setDeliveryMethod(DeliveryMethodEnum.LOGISTICS.name()); + } + cartVO.setSkuList(storeCart.getValue()); + try { + //筛选属于当前店铺的优惠券 + storeCart.getValue().forEach(i -> i.getPromotionMap().forEach((key, value) -> { + if (key.contains(PromotionTypeEnum.COUPON.name())) { + JSONObject promotionsObj = JSONUtil.parseObj(value); + Coupon coupon = JSONUtil.toBean(promotionsObj, Coupon.class); + if (key.contains(PromotionTypeEnum.COUPON.name()) && coupon.getStoreId().equals(storeCart.getKey())) { + cartVO.getCanReceiveCoupon().add(new CouponVO(coupon)); + } } - } - })); - } catch (Exception e) { - log.error("筛选属于当前店铺的优惠券发生异常!", e); + })); + } catch (Exception e) { + log.error("筛选属于当前店铺的优惠券发生异常!", e); + } + storeCart.getValue().stream().filter(i -> Boolean.TRUE.equals(i.getChecked())).findFirst().ifPresent(cartSkuVO -> cartVO.setChecked(true)); + cartList.add(cartVO); } - storeCart.getValue().stream().filter(i -> Boolean.TRUE.equals(i.getChecked())).findFirst().ifPresent(cartSkuVO -> cartVO.setChecked(true)); - cartList.add(cartVO); } + tradeDTO.setCartList(cartList); } - tradeDTO.setCartList(cartList); + } /** diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java index 799c62b8..a25fef51 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java @@ -3,10 +3,12 @@ package cn.lili.modules.order.cart.render.impl; import cn.lili.common.utils.CurrencyUtil; import cn.lili.modules.member.entity.dos.MemberAddress; import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; import cn.lili.modules.order.cart.entity.enums.RenderStepEnums; import cn.lili.modules.order.cart.entity.vo.CartSkuVO; import cn.lili.modules.order.cart.render.CartRenderStep; import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import cn.lili.modules.store.entity.dos.StoreAddress; import cn.lili.modules.store.entity.dto.FreightTemplateChildDTO; import cn.lili.modules.store.entity.enums.FreightTemplateEnum; import cn.lili.modules.store.entity.vos.FreightTemplateVO; @@ -42,71 +44,77 @@ public class SkuFreightRender implements CartRenderStep { List cartSkuVOS = tradeDTO.getCheckedSkuList(); //会员收货地址问题处理 MemberAddress memberAddress = tradeDTO.getMemberAddress(); + StoreAddress storeAddress = tradeDTO.getStoreAddress(); //如果收货地址为空,则抛出异常 - if (memberAddress == null) { + if (memberAddress == null && storeAddress == null) { return; } - //运费分组信息 - Map> freightGroups = freightTemplateGrouping(cartSkuVOS); + //选择物流的时候计算价格 + if(DeliveryMethodEnum.LOGISTICS.name().equals(tradeDTO.getCartList().get(0).getDeliveryMethod())){ + if (memberAddress != null) { + //运费分组信息 + Map> freightGroups = freightTemplateGrouping(cartSkuVOS); - //循环运费模版 - for (Map.Entry> freightTemplateGroup : freightGroups.entrySet()) { + //循环运费模版 + for (Map.Entry> freightTemplateGroup : freightGroups.entrySet()) { - //商品id列表 - List skuIds = freightTemplateGroup.getValue(); + //商品id列表 + List skuIds = freightTemplateGroup.getValue(); - //当前购物车商品列表 - List currentCartSkus = cartSkuVOS.stream().filter(item -> skuIds.contains(item.getGoodsSku().getId())).collect(Collectors.toList()); + //当前购物车商品列表 + List currentCartSkus = cartSkuVOS.stream().filter(item -> skuIds.contains(item.getGoodsSku().getId())).collect(Collectors.toList()); - //寻找对应对商品运费计算模版 - FreightTemplateVO freightTemplate = freightTemplateService.getFreightTemplate(freightTemplateGroup.getKey()); - if (freightTemplate != null - && freightTemplate.getFreightTemplateChildList() != null - && !freightTemplate.getFreightTemplateChildList().isEmpty()) { - //店铺模版免运费则跳过 - if (freightTemplate.getPricingMethod().equals(FreightTemplateEnum.FREE.name())) { - break; - } + //寻找对应对商品运费计算模版 + FreightTemplateVO freightTemplate = freightTemplateService.getFreightTemplate(freightTemplateGroup.getKey()); + if (freightTemplate != null + && freightTemplate.getFreightTemplateChildList() != null + && !freightTemplate.getFreightTemplateChildList().isEmpty()) { + //店铺模版免运费则跳过 + if (freightTemplate.getPricingMethod().equals(FreightTemplateEnum.FREE.name())) { + break; + } - //运费模版 - FreightTemplateChild freightTemplateChild = null; + //运费模版 + FreightTemplateChild freightTemplateChild = null; - //获取市级别id匹配运费模版 - String addressId = memberAddress.getConsigneeAddressIdPath().split(",")[1]; - for (FreightTemplateChild templateChild : freightTemplate.getFreightTemplateChildList()) { - //模版匹配判定 - if (templateChild.getAreaId().contains(addressId)) { - freightTemplateChild = templateChild; - break; + //获取市级别id匹配运费模版 + String addressId = memberAddress.getConsigneeAddressIdPath().split(",")[1]; + for (FreightTemplateChild templateChild : freightTemplate.getFreightTemplateChildList()) { + //模版匹配判定 + if (templateChild.getAreaId().contains(addressId)) { + freightTemplateChild = templateChild; + break; + } + } + //如果没有匹配到物流规则,则说明不支持配送 + if (freightTemplateChild == null) { + if (tradeDTO.getNotSupportFreight() == null) { + tradeDTO.setNotSupportFreight(new ArrayList<>()); + } + tradeDTO.getNotSupportFreight().addAll(currentCartSkus); + continue; + } + + //物流规则模型创立 + FreightTemplateChildDTO freightTemplateChildDTO = new FreightTemplateChildDTO(freightTemplateChild); + //模型写入运费模版设置的计费方式 + freightTemplateChildDTO.setPricingMethod(freightTemplate.getPricingMethod()); + + //计算运费总数 + Double count = currentCartSkus.stream().mapToDouble(item -> + // 根据计费规则 累加计费基数 + freightTemplateChildDTO.getPricingMethod().equals(FreightTemplateEnum.NUM.name()) ? + item.getNum().doubleValue() : + CurrencyUtil.mul(item.getNum(), item.getGoodsSku().getWeight()) + ).sum(); + + //计算运费 + Double countFreight = countFreight(count, freightTemplateChildDTO); + + //写入SKU运费 + resetFreightPrice(FreightTemplateEnum.valueOf(freightTemplateChildDTO.getPricingMethod()), count, countFreight, currentCartSkus); } } - //如果没有匹配到物流规则,则说明不支持配送 - if (freightTemplateChild == null) { - if (tradeDTO.getNotSupportFreight() == null) { - tradeDTO.setNotSupportFreight(new ArrayList<>()); - } - tradeDTO.getNotSupportFreight().addAll(currentCartSkus); - continue; - } - - //物流规则模型创立 - FreightTemplateChildDTO freightTemplateChildDTO = new FreightTemplateChildDTO(freightTemplateChild); - //模型写入运费模版设置的计费方式 - freightTemplateChildDTO.setPricingMethod(freightTemplate.getPricingMethod()); - - //计算运费总数 - Double count = currentCartSkus.stream().mapToDouble(item -> - // 根据计费规则 累加计费基数 - freightTemplateChildDTO.getPricingMethod().equals(FreightTemplateEnum.NUM.name()) ? - item.getNum().doubleValue() : - CurrencyUtil.mul(item.getNum(), item.getGoodsSku().getWeight()) - ).sum(); - - //计算运费 - Double countFreight = countFreight(count, freightTemplateChildDTO); - - //写入SKU运费 - resetFreightPrice(FreightTemplateEnum.valueOf(freightTemplateChildDTO.getPricingMethod()), count, countFreight, currentCartSkus); } } } diff --git a/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java b/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java index d11d8aa8..2d525af4 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java @@ -7,6 +7,8 @@ import cn.lili.modules.order.cart.entity.vo.TradeParams; import cn.lili.modules.order.order.entity.dos.Trade; import cn.lili.modules.order.order.entity.vo.ReceiptVO; +import java.util.List; + /** * 购物车业务层 * @@ -111,6 +113,14 @@ public interface CartService { */ void shippingAddress(String shippingAddressId, String way); + /** + * 选择自提地址 + * + * @param shopAddressId 收货地址id + * @param way 购物车类型 + */ + void shippingSelfAddress(String shopAddressId, String way); + /** * 选择发票 * @@ -123,11 +133,10 @@ public interface CartService { /** * 选择配送方式 * - * @param storeId 店铺id * @param deliveryMethod 配送方式 * @param way 购物车类型 */ - void shippingMethod(String storeId, String deliveryMethod, String way); + void shippingMethod(String deliveryMethod, String way); /** * 获取购物车商品数量 @@ -160,4 +169,11 @@ public interface CartService { * @return 交易信息 */ Trade createTrade(TradeParams tradeParams); + + /*** + * 获取可使用的配送方式 + * @param way + * @return + */ + List shippingMethodList(String way); } diff --git a/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java index 485bb249..c0970787 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java @@ -46,6 +46,10 @@ import cn.lili.modules.promotion.service.PointsGoodsService; import cn.lili.modules.promotion.service.PromotionGoodsService; import cn.lili.modules.search.entity.dos.EsGoodsIndex; import cn.lili.modules.search.service.EsGoodsSearchService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.dos.StoreAddress; +import cn.lili.modules.store.service.StoreAddressService; +import cn.lili.modules.store.service.StoreService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -116,6 +120,12 @@ public class CartServiceImpl implements CartService { @Autowired private WholesaleService wholesaleService; + @Autowired + private StoreService storeService; + + @Autowired + private StoreAddressService storeAddressService; + @Override public void add(String skuId, Integer num, String cartType, Boolean cover) { AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser()); @@ -438,6 +448,20 @@ public class CartServiceImpl implements CartService { this.resetTradeDTO(tradeDTO); } + @Override + public void shippingSelfAddress(String shopAddressId, String way) { + //默认购物车 + CartTypeEnum cartTypeEnum = CartTypeEnum.CART; + if (CharSequenceUtil.isNotEmpty(way)) { + cartTypeEnum = CartTypeEnum.valueOf(way); + } + + TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + StoreAddress storeAddress = storeAddressService.getById(shopAddressId); + tradeDTO.setStoreAddress(storeAddress); + this.resetTradeDTO(tradeDTO); + } + /** * 选择发票 * @@ -459,23 +483,21 @@ public class CartServiceImpl implements CartService { /** * 选择配送方式 * - * @param storeId 店铺id * @param deliveryMethod 配送方式 * @param way 购物车类型 */ @Override - public void shippingMethod(String storeId, String deliveryMethod, String way) { + public void shippingMethod(String deliveryMethod, String way) { CartTypeEnum cartTypeEnum = CartTypeEnum.CART; if (CharSequenceUtil.isNotEmpty(way)) { cartTypeEnum = CartTypeEnum.valueOf(way); } - TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + TradeDTO tradeDTO = this.getCheckedTradeDTO(cartTypeEnum); for (CartVO cartVO : tradeDTO.getCartList()) { - if (cartVO.getStoreId().equals(storeId)) { - cartVO.setDeliveryMethod(DeliveryMethodEnum.valueOf(deliveryMethod).name()); - } + cartVO.setDeliveryMethod(DeliveryMethodEnum.valueOf(deliveryMethod).name()); } this.resetTradeDTO(tradeDTO); + TradeDTO neTradeDTO = (TradeDTO) cache.get(this.getOriginKey(cartTypeEnum)); } /** @@ -547,8 +569,10 @@ public class CartServiceImpl implements CartService { tradeDTO.setStoreRemark(tradeParams.getRemark()); tradeDTO.setParentOrderSn(tradeParams.getParentOrderSn()); //订单无收货地址校验 - if (tradeDTO.getMemberAddress() == null) { - throw new ServiceException(ResultCode.MEMBER_ADDRESS_NOT_EXIST); + if(tradeDTO.getStoreAddress() == null){ + if (tradeDTO.getMemberAddress() == null) { + throw new ServiceException(ResultCode.MEMBER_ADDRESS_NOT_EXIST); + } } //构建交易 Trade trade = tradeBuilder.createTrade(tradeDTO); @@ -556,6 +580,22 @@ public class CartServiceImpl implements CartService { return trade; } + @Override + public List shippingMethodList(String way) { + List list = new ArrayList(); + list.add(DeliveryMethodEnum.LOGISTICS.name()); + TradeDTO tradeDTO = this.getCheckedTradeDTO(CartTypeEnum.valueOf(way)); + if(tradeDTO.getCartList().size()==1){ + for (CartVO cartVO : tradeDTO.getCartList()) { + Store store = storeService.getById(cartVO.getStoreId()); + if(store.getSelfPickFlag() != null && store.getSelfPickFlag()){ + list.add(DeliveryMethodEnum.SELF_PICK_UP.name()); + } + } + } + return list; + } + /** * 获取购物车类型 diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java index f185d17d..a937e125 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java @@ -210,6 +210,15 @@ public class Order extends BaseEntity { @ApiModelProperty(value = "qrCode 实物为提货码 虚拟货物为账号") private String qrCode; + @ApiModelProperty(value = "自提点地址") + private String storeAddressPath; + + @ApiModelProperty(value = "自提点电话") + private String storeAddressMobile; + + @ApiModelProperty(value = "自提点地址经纬度") + private String storeAddressCenter; + /** * 构建订单 * @@ -233,11 +242,19 @@ public class Order extends BaseEntity { this.setRemark(cartVO.getRemark()); this.setFreightPrice(tradeDTO.getPriceDetailDTO().getFreightPrice()); //会员收件信息 - this.setConsigneeAddressIdPath(tradeDTO.getMemberAddress().getConsigneeAddressIdPath()); - this.setConsigneeAddressPath(tradeDTO.getMemberAddress().getConsigneeAddressPath()); - this.setConsigneeDetail(tradeDTO.getMemberAddress().getDetail()); - this.setConsigneeMobile(tradeDTO.getMemberAddress().getMobile()); - this.setConsigneeName(tradeDTO.getMemberAddress().getName()); + if(DeliveryMethodEnum.LOGISTICS.name().equals(cartVO.getDeliveryMethod())){ + this.setConsigneeAddressIdPath(tradeDTO.getMemberAddress().getConsigneeAddressIdPath()); + this.setConsigneeAddressPath(tradeDTO.getMemberAddress().getConsigneeAddressPath()); + this.setConsigneeDetail(tradeDTO.getMemberAddress().getDetail()); + this.setConsigneeMobile(tradeDTO.getMemberAddress().getMobile()); + this.setConsigneeName(tradeDTO.getMemberAddress().getName()); + } + //自提点信息 + if(DeliveryMethodEnum.SELF_PICK_UP.name().equals(cartVO.getDeliveryMethod())){ + this.setStoreAddressPath(tradeDTO.getStoreAddress().getAddress()); + this.setStoreAddressMobile(tradeDTO.getStoreAddress().getMobile()); + this.setStoreAddressCenter(tradeDTO.getStoreAddress().getCenter()); + } //平台优惠券判定 if (tradeDTO.getPlatformCoupon() != null) { this.setUsePlatformMemberCouponId(tradeDTO.getPlatformCoupon().getMemberCoupon().getId()); diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java index 6d9f4906..8edcaccf 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java @@ -16,6 +16,7 @@ public enum OrderStatusEnum { UNDELIVERED("待发货"), DELIVERED("已发货"), COMPLETED("已完成"), + STAY_PICKED_UP("待自提"), /** * 虚拟订单需要核验商品 */ diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java index 6ef7ca59..dd480e32 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java @@ -63,7 +63,7 @@ public class AllowOperation implements Serializable { } //新订单 - if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name())) { + if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name(), OrderStatusEnum.STAY_PICKED_UP.name())) { this.cancel = true; } //新订单,允许支付 diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java index 3740bb08..507d797f 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java @@ -183,6 +183,15 @@ public interface OrderService extends IService { */ Order take(String orderSn, String verificationCode); + + /** + * 订单核验 + * + * @param verificationCode 验证码 + * @return 订单 + */ + Order take(String verificationCode); + /** * 根据核验码获取订单信息 * diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java index 757b2a95..9ecb22ac 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java @@ -16,10 +16,12 @@ import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.common.security.OperationalJudgment; import cn.lili.common.security.context.UserContext; import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.ObjectUtil; import cn.lili.common.utils.SnowFlake; import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage; import cn.lili.modules.member.entity.dto.MemberAddressDTO; import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; import cn.lili.modules.order.order.aop.OrderLogPoint; import cn.lili.modules.order.order.entity.dos.*; import cn.lili.modules.order.order.entity.dto.OrderBatchDeliverDTO; @@ -290,12 +292,13 @@ public class OrderServiceImpl extends ServiceImpl implements Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); //如果订单促销类型不为空&&订单是拼团订单,并且订单未成团,则抛出异常 if (OrderPromotionTypeEnum.PINTUAN.name().equals(order.getOrderPromotionType()) - && !order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) { + && !CharSequenceUtil.equalsAny(order.getOrderStatus(),OrderStatusEnum.UNDELIVERED.name(),OrderStatusEnum.STAY_PICKED_UP.name())) { throw new ServiceException(ResultCode.ORDER_CAN_NOT_CANCEL); } if (CharSequenceUtil.equalsAny(order.getOrderStatus(), OrderStatusEnum.UNDELIVERED.name(), OrderStatusEnum.UNPAID.name(), + OrderStatusEnum.STAY_PICKED_UP.name(), OrderStatusEnum.PAID.name())) { order.setOrderStatus(OrderStatusEnum.CANCELLED.name()); @@ -472,11 +475,24 @@ public class OrderServiceImpl extends ServiceImpl implements return order; } + @Override + public Order take(String verificationCode) { + String storeId = OperationalJudgment.judgment(UserContext.getCurrentUser()).getStoreId(); + Order order = this.getOne(new LambdaQueryWrapper().eq(Order::getVerificationCode, verificationCode).eq(Order::getStoreId, storeId)); + if(order == null){ + throw new ServiceException(ResultCode.ORDER_NOT_EXIST); + } + order.setOrderStatus(OrderStatusEnum.COMPLETED.name()); + //订单完成 + this.complete(order.getSn()); + return order; + } + @Override public Order getOrderByVerificationCode(String verificationCode) { String storeId = Objects.requireNonNull(UserContext.getCurrentUser()).getStoreId(); return this.getOne(new LambdaQueryWrapper() - .eq(Order::getOrderStatus, OrderStatusEnum.TAKE.name()) + .in(Order::getOrderStatus, OrderStatusEnum.TAKE.name(),OrderStatusEnum.STAY_PICKED_UP.name()) .eq(Order::getStoreId, storeId) .eq(Order::getVerificationCode, verificationCode)); } @@ -938,13 +954,20 @@ public class OrderServiceImpl extends ServiceImpl implements */ @Transactional(rollbackFor = Exception.class) public void normalOrderConfirm(String orderSn) { + OrderStatusEnum orderStatusEnum = null; + Order order = this.getBySn(orderSn); + if(DeliveryMethodEnum.SELF_PICK_UP.name().equals(order.getDeliveryMethod())){ + orderStatusEnum = OrderStatusEnum.STAY_PICKED_UP; + }else if (DeliveryMethodEnum.LOGISTICS.name().equals(order.getDeliveryMethod())){ + orderStatusEnum = OrderStatusEnum.UNDELIVERED; + } //修改订单 this.update(new LambdaUpdateWrapper() .eq(Order::getSn, orderSn) - .set(Order::getOrderStatus, OrderStatusEnum.UNDELIVERED.name())); + .set(Order::getOrderStatus, orderStatusEnum.name())); //修改订单 OrderMessage orderMessage = new OrderMessage(); - orderMessage.setNewStatus(OrderStatusEnum.UNDELIVERED); + orderMessage.setNewStatus(orderStatusEnum); orderMessage.setOrderSn(orderSn); this.sendUpdateStatusMessage(orderMessage); } diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java index 6094598d..199eaf65 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java @@ -1 +1 @@ -package cn.lili.modules.order.order.serviceimpl; import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.modules.member.entity.dos.MemberAddress; import cn.lili.modules.member.entity.enums.PointTypeEnum; import cn.lili.modules.member.service.MemberService; import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; import cn.lili.modules.order.cart.entity.dto.TradeDTO; import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; import cn.lili.modules.order.cart.entity.vo.CartVO; import cn.lili.modules.order.order.entity.dos.Order; import cn.lili.modules.order.order.entity.dos.Trade; import cn.lili.modules.order.order.entity.enums.PayStatusEnum; import cn.lili.modules.order.order.mapper.TradeMapper; import cn.lili.modules.order.order.service.OrderService; import cn.lili.modules.order.order.service.TradeService; import cn.lili.modules.promotion.service.CouponService; import cn.lili.modules.promotion.service.KanjiaActivityService; import cn.lili.modules.promotion.service.MemberCouponService; import cn.lili.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.rocketmq.tags.OrderTagsEnum; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * 交易业务层实现 * * @author Chopper * @since 2020/11/17 7:39 下午 */ @Service public class TradeServiceImpl extends ServiceImpl implements TradeService { /** * 缓存 */ @Autowired private Cache cache; /** * 订单 */ @Autowired private OrderService orderService; /** * 会员 */ @Autowired private MemberService memberService; /** * 优惠券 */ @Autowired private CouponService couponService; /** * 会员优惠券 */ @Autowired private MemberCouponService memberCouponService; /** * 砍价 */ @Autowired private KanjiaActivityService kanjiaActivityService; /** * RocketMQ */ @Autowired private RocketMQTemplate rocketMQTemplate; /** * RocketMQ 配置 */ @Autowired private RocketmqCustomProperties rocketmqCustomProperties; @Override @Transactional(rollbackFor = Exception.class) public Trade createTrade(TradeDTO tradeDTO) { //创建订单预校验 createTradeCheck(tradeDTO); Trade trade = new Trade(tradeDTO); String key = CachePrefix.TRADE.getPrefix() + trade.getSn(); //优惠券预处理 couponPretreatment(tradeDTO); //积分预处理 pointPretreatment(tradeDTO); //添加交易 this.save(trade); //添加订单 orderService.intoDB(tradeDTO); //砍价订单处理 kanjiaPretreatment(tradeDTO); //写入缓存,给消费者调用 cache.put(key, JSONUtil.toJsonStr(tradeDTO)); //构建订单创建消息 String destination = rocketmqCustomProperties.getOrderTopic() + ":" + OrderTagsEnum.ORDER_CREATE.name(); //发送订单创建消息 rocketMQTemplate.asyncSend(destination, key, RocketmqSendCallbackBuilder.commonCallback()); return trade; } /** * 创建订单最后一步校验 * * @param tradeDTO 购物车视图 */ private void createTradeCheck(TradeDTO tradeDTO) { //创建订单如果没有收获地址, MemberAddress memberAddress = tradeDTO.getMemberAddress(); if (memberAddress == null) { throw new ServiceException(ResultCode.MEMBER_ADDRESS_NOT_EXIST); } //订单配送区域校验 if (tradeDTO.getNotSupportFreight() != null && !tradeDTO.getNotSupportFreight().isEmpty()) { StringBuilder stringBuilder = new StringBuilder("包含商品有-"); tradeDTO.getNotSupportFreight().forEach(sku -> stringBuilder.append(sku.getGoodsSku().getGoodsName())); throw new ServiceException(ResultCode.ORDER_NOT_SUPPORT_DISTRIBUTION, stringBuilder.toString()); } if (tradeDTO.getCartList().stream().noneMatch(CartVO::getChecked)) { throw new ServiceException(ResultCode.ORDER_NOT_EXIST_VALID); } } @Override public Trade getBySn(String sn) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Trade::getSn, sn); return this.getOne(queryWrapper); } @Override @Transactional(rollbackFor = Exception.class) public void payTrade(String tradeSn, String paymentName, String receivableNo) { LambdaQueryWrapper orderQueryWrapper = new LambdaQueryWrapper<>(); orderQueryWrapper.eq(Order::getTradeSn, tradeSn); List orders = orderService.list(orderQueryWrapper); for (Order order : orders) { orderService.payOrder(order.getSn(), paymentName, receivableNo); } Trade trade = this.getBySn(tradeSn); trade.setPayStatus(PayStatusEnum.PAID.name()); this.saveOrUpdate(trade); } /** * 优惠券预处理 * 下单同时,扣除优惠券 * * @param tradeDTO 购物车视图 */ private void couponPretreatment(TradeDTO tradeDTO) { List memberCouponDTOList = new ArrayList<>(); if (null != tradeDTO.getPlatformCoupon()) { memberCouponDTOList.add(tradeDTO.getPlatformCoupon()); } Collection storeCoupons = tradeDTO.getStoreCoupons().values(); if (!storeCoupons.isEmpty()) { memberCouponDTOList.addAll(storeCoupons); } List ids = memberCouponDTOList.stream().map(e -> e.getMemberCoupon().getId()).collect(Collectors.toList()); memberCouponService.used(tradeDTO.getMemberId(), ids); memberCouponDTOList.forEach(e -> couponService.usedCoupon(e.getMemberCoupon().getCouponId(), 1)); } /** * 创建交易,积分处理 * * @param tradeDTO 购物车视图 */ private void pointPretreatment(TradeDTO tradeDTO) { //需要支付积分 if (tradeDTO.getPriceDetailDTO() != null && tradeDTO.getPriceDetailDTO().getPayPoint() != null && tradeDTO.getPriceDetailDTO().getPayPoint() > 0) { StringBuilder orderSns = new StringBuilder(); for (CartVO item : tradeDTO.getCartList()) { orderSns.append(item.getSn()); } boolean result = memberService.updateMemberPoint(tradeDTO.getPriceDetailDTO().getPayPoint(), PointTypeEnum.REDUCE.name(), tradeDTO.getMemberId(), "订单【" + orderSns + "】创建,积分扣减"); if (!result) { throw new ServiceException(ResultCode.PAY_POINT_ENOUGH); } } } /** * 创建交易、砍价处理 * * @param tradeDTO 购物车视图 */ private void kanjiaPretreatment(TradeDTO tradeDTO) { if (tradeDTO.getCartTypeEnum().equals(CartTypeEnum.KANJIA)) { String kanjiaId = tradeDTO.getSkuList().get(0).getKanjiaId(); kanjiaActivityService.endKanjiaActivity(kanjiaId); } } } \ No newline at end of file +package cn.lili.modules.order.order.serviceimpl; import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.modules.member.entity.dos.MemberAddress; import cn.lili.modules.member.entity.enums.PointTypeEnum; import cn.lili.modules.member.service.MemberService; import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; import cn.lili.modules.order.cart.entity.dto.TradeDTO; import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; import cn.lili.modules.order.cart.entity.vo.CartVO; import cn.lili.modules.order.order.entity.dos.Order; import cn.lili.modules.order.order.entity.dos.Trade; import cn.lili.modules.order.order.entity.enums.PayStatusEnum; import cn.lili.modules.order.order.mapper.TradeMapper; import cn.lili.modules.order.order.service.OrderService; import cn.lili.modules.order.order.service.TradeService; import cn.lili.modules.promotion.service.CouponService; import cn.lili.modules.promotion.service.KanjiaActivityService; import cn.lili.modules.promotion.service.MemberCouponService; import cn.lili.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.rocketmq.tags.OrderTagsEnum; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * 交易业务层实现 * * @author Chopper * @since 2020/11/17 7:39 下午 */ @Service public class TradeServiceImpl extends ServiceImpl implements TradeService { /** * 缓存 */ @Autowired private Cache cache; /** * 订单 */ @Autowired private OrderService orderService; /** * 会员 */ @Autowired private MemberService memberService; /** * 优惠券 */ @Autowired private CouponService couponService; /** * 会员优惠券 */ @Autowired private MemberCouponService memberCouponService; /** * 砍价 */ @Autowired private KanjiaActivityService kanjiaActivityService; /** * RocketMQ */ @Autowired private RocketMQTemplate rocketMQTemplate; /** * RocketMQ 配置 */ @Autowired private RocketmqCustomProperties rocketmqCustomProperties; @Override @Transactional(rollbackFor = Exception.class) public Trade createTrade(TradeDTO tradeDTO) { //创建订单预校验 createTradeCheck(tradeDTO); Trade trade = new Trade(tradeDTO); String key = CachePrefix.TRADE.getPrefix() + trade.getSn(); //优惠券预处理 couponPretreatment(tradeDTO); //积分预处理 pointPretreatment(tradeDTO); //添加交易 this.save(trade); //添加订单 orderService.intoDB(tradeDTO); //砍价订单处理 kanjiaPretreatment(tradeDTO); //写入缓存,给消费者调用 cache.put(key, JSONUtil.toJsonStr(tradeDTO)); //构建订单创建消息 String destination = rocketmqCustomProperties.getOrderTopic() + ":" + OrderTagsEnum.ORDER_CREATE.name(); //发送订单创建消息 rocketMQTemplate.asyncSend(destination, key, RocketmqSendCallbackBuilder.commonCallback()); return trade; } /** * 创建订单最后一步校验 * * @param tradeDTO 购物车视图 */ private void createTradeCheck(TradeDTO tradeDTO) { if(tradeDTO.getStoreAddress() == null){ //创建订单如果没有收获地址, MemberAddress memberAddress = tradeDTO.getMemberAddress(); if (memberAddress == null) { throw new ServiceException(ResultCode.MEMBER_ADDRESS_NOT_EXIST); } //订单配送区域校验 if (tradeDTO.getNotSupportFreight() != null && !tradeDTO.getNotSupportFreight().isEmpty()) { StringBuilder stringBuilder = new StringBuilder("包含商品有-"); tradeDTO.getNotSupportFreight().forEach(sku -> stringBuilder.append(sku.getGoodsSku().getGoodsName())); throw new ServiceException(ResultCode.ORDER_NOT_SUPPORT_DISTRIBUTION, stringBuilder.toString()); } if (tradeDTO.getCartList().stream().noneMatch(CartVO::getChecked)) { throw new ServiceException(ResultCode.ORDER_NOT_EXIST_VALID); } } } @Override public Trade getBySn(String sn) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Trade::getSn, sn); return this.getOne(queryWrapper); } @Override @Transactional(rollbackFor = Exception.class) public void payTrade(String tradeSn, String paymentName, String receivableNo) { LambdaQueryWrapper orderQueryWrapper = new LambdaQueryWrapper<>(); orderQueryWrapper.eq(Order::getTradeSn, tradeSn); List orders = orderService.list(orderQueryWrapper); for (Order order : orders) { orderService.payOrder(order.getSn(), paymentName, receivableNo); } Trade trade = this.getBySn(tradeSn); trade.setPayStatus(PayStatusEnum.PAID.name()); this.saveOrUpdate(trade); } /** * 优惠券预处理 * 下单同时,扣除优惠券 * * @param tradeDTO 购物车视图 */ private void couponPretreatment(TradeDTO tradeDTO) { List memberCouponDTOList = new ArrayList<>(); if (null != tradeDTO.getPlatformCoupon()) { memberCouponDTOList.add(tradeDTO.getPlatformCoupon()); } Collection storeCoupons = tradeDTO.getStoreCoupons().values(); if (!storeCoupons.isEmpty()) { memberCouponDTOList.addAll(storeCoupons); } List ids = memberCouponDTOList.stream().map(e -> e.getMemberCoupon().getId()).collect(Collectors.toList()); memberCouponService.used(tradeDTO.getMemberId(), ids); memberCouponDTOList.forEach(e -> couponService.usedCoupon(e.getMemberCoupon().getCouponId(), 1)); } /** * 创建交易,积分处理 * * @param tradeDTO 购物车视图 */ private void pointPretreatment(TradeDTO tradeDTO) { //需要支付积分 if (tradeDTO.getPriceDetailDTO() != null && tradeDTO.getPriceDetailDTO().getPayPoint() != null && tradeDTO.getPriceDetailDTO().getPayPoint() > 0) { StringBuilder orderSns = new StringBuilder(); for (CartVO item : tradeDTO.getCartList()) { orderSns.append(item.getSn()); } boolean result = memberService.updateMemberPoint(tradeDTO.getPriceDetailDTO().getPayPoint(), PointTypeEnum.REDUCE.name(), tradeDTO.getMemberId(), "订单【" + orderSns + "】创建,积分扣减"); if (!result) { throw new ServiceException(ResultCode.PAY_POINT_ENOUGH); } } } /** * 创建交易、砍价处理 * * @param tradeDTO 购物车视图 */ private void kanjiaPretreatment(TradeDTO tradeDTO) { if (tradeDTO.getCartTypeEnum().equals(CartTypeEnum.KANJIA)) { String kanjiaId = tradeDTO.getSkuList().get(0).getKanjiaId(); kanjiaActivityService.endKanjiaActivity(kanjiaId); } } } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java b/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java index 7257ca22..edc55c6e 100644 --- a/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java +++ b/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java @@ -1,6 +1,7 @@ package cn.lili.modules.permission.service; +import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.token.Token; import cn.lili.modules.permission.entity.dos.AdminUser; import cn.lili.modules.permission.entity.dto.AdminUserDTO; @@ -99,4 +100,11 @@ public interface AdminUserService extends IService { */ Token refreshToken(String refreshToken); + /** + * 登出 + * + * @param userEnums token角色类型 + */ + void logout(UserEnums userEnums); + } diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java index 53ec3049..bc4f55d4 100644 --- a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java @@ -1,10 +1,13 @@ package cn.lili.modules.permission.serviceimpl; import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.cache.Cache; +import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.security.AuthUser; import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.token.Token; import cn.lili.common.utils.BeanUtil; import cn.lili.common.utils.StringUtils; @@ -53,6 +56,10 @@ public class AdminUserServiceImpl extends ServiceImpl { */ void updateStoreCollectionNum(CollectionDTO collectionDTO); + /** + * 重新生成所有店铺 + */ void storeToClerk(); + + /** + * 店铺获取该会员的访问记录 + * @param memberId 会员Id + * @return + */ + List getToMemberHistory(String memberId); } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java index 7c64f686..efb479ad 100644 --- a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java @@ -25,6 +25,8 @@ import cn.lili.modules.store.service.StoreDetailService; import cn.lili.modules.store.service.StoreService; import cn.lili.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.rocketmq.tags.GoodsTagsEnum; +import cn.lili.rocketmq.tags.MemberTagsEnum; +import cn.lili.rocketmq.tags.StoreTagsEnum; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -99,6 +101,9 @@ public class StoreDetailServiceImpl extends ServiceImpl implements */ @Autowired private GoodsService goodsService; + + @Autowired + private GoodsSkuService goodsSkuService; /** * 店铺详情 */ @Autowired private StoreDetailService storeDetailService; + @Autowired + private RocketmqCustomProperties rocketmqCustomProperties; + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + @Autowired + private FootprintService footprintService; + @Autowired private Cache cache; @@ -159,7 +179,11 @@ public class StoreServiceImpl extends ServiceImpl implements if (result) { storeDetailService.updateStoreGoodsInfo(store); } + String destination = rocketmqCustomProperties.getStoreTopic() + ":" + StoreTagsEnum.EDIT_STORE_SETTING.name(); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, store, RocketmqSendCallbackBuilder.commonCallback()); } + cache.remove(CachePrefix.STORE.getPrefix() + storeEditDTO.getStoreId()); return store; } @@ -352,6 +376,18 @@ public class StoreServiceImpl extends ServiceImpl implements clerkService.saveBatch(clerkList); } + @Override + public List getToMemberHistory(String memberId) { + AuthUser currentUser = UserContext.getCurrentUser(); + List skuIdList = new ArrayList<>(); + for (FootPrint footPrint : footprintService.list(new LambdaUpdateWrapper().eq(FootPrint::getStoreId, currentUser.getStoreId()).eq(FootPrint::getMemberId, memberId))) { + if(footPrint.getSkuId() != null){ + skuIdList.add(footPrint.getSkuId()); + } + } + return goodsSkuService.getGoodsSkuByIdFromCache(skuIdList); + } + /** * 获取当前登录操作的店铺 * diff --git a/framework/src/main/java/cn/lili/mybatis/BaseTenantEntity.java b/framework/src/main/java/cn/lili/mybatis/BaseTenantEntity.java new file mode 100644 index 00000000..c0fc647c --- /dev/null +++ b/framework/src/main/java/cn/lili/mybatis/BaseTenantEntity.java @@ -0,0 +1,24 @@ +package cn.lili.mybatis; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 租户超级类 + * + * @author Chopper + * @version v1.0 + * @since 2020/8/20 14:34 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public abstract class BaseTenantEntity extends BaseEntity { + + @ApiModelProperty(value = "租户id", hidden = true) + private String tenantId; + +} diff --git a/framework/src/main/java/cn/lili/rocketmq/tags/MemberTagsEnum.java b/framework/src/main/java/cn/lili/rocketmq/tags/MemberTagsEnum.java index a179f5a8..bda332b7 100644 --- a/framework/src/main/java/cn/lili/rocketmq/tags/MemberTagsEnum.java +++ b/framework/src/main/java/cn/lili/rocketmq/tags/MemberTagsEnum.java @@ -23,6 +23,10 @@ public enum MemberTagsEnum { * 会员提现 */ MEMBER_WITHDRAWAL("会员提现"), + /** + * 会员信息更改 + */ + MEMBER_INFO_EDIT("会员信息更改"), /** * 会员积分变动 */ diff --git a/framework/src/main/java/cn/lili/rocketmq/tags/StoreTagsEnum.java b/framework/src/main/java/cn/lili/rocketmq/tags/StoreTagsEnum.java new file mode 100644 index 00000000..f18a7428 --- /dev/null +++ b/framework/src/main/java/cn/lili/rocketmq/tags/StoreTagsEnum.java @@ -0,0 +1,20 @@ +package cn.lili.rocketmq.tags; + +/** + * @author chc + * @since 2022/6/2114:46 + */ +public enum StoreTagsEnum { + + EDIT_STORE_SETTING("修改商家设置"); + + private final String description; + + StoreTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/im-api/pom.xml b/im-api/pom.xml new file mode 100644 index 00000000..7f823cf4 --- /dev/null +++ b/im-api/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + im-api + + + cn.lili + lili-shop-parent + ${revision} + ../pom.xml + + + + + cn.lili + framework + ${revision} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + \ No newline at end of file diff --git a/im-api/src/main/java/cn/lili/ImApiApplication.java b/im-api/src/main/java/cn/lili/ImApiApplication.java new file mode 100644 index 00000000..67078506 --- /dev/null +++ b/im-api/src/main/java/cn/lili/ImApiApplication.java @@ -0,0 +1,31 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + + +/** + * @author liushuai + */ +@SpringBootApplication +public class ImApiApplication { + + public static void main(String[] args) { + SpringApplication.run(ImApiApplication.class, args); + } + + /** + * 如果使用独立的servlet容器, + * 而不是直接使用springboot的内置容器, + * 就不要注入ServerEndpointExporter, + * 因为它将由容器自己提供和管理 + * + * @return + */ + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/im-api/src/main/java/cn/lili/controller/goods/GoodsImController.java b/im-api/src/main/java/cn/lili/controller/goods/GoodsImController.java new file mode 100644 index 00000000..4e5aa3c7 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/goods/GoodsImController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.search.service.EsGoodsSearchService; +import cn.lili.modules.search.service.HotWordsService; +import cn.lili.modules.statistics.aop.PageViewPoint; +import cn.lili.modules.statistics.aop.enums.PageViewEnum; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +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.validation.constraints.NotNull; +import java.util.List; +import java.util.Map; + +/** + * IM端,商品接口 + * + * @author chc + * @since 2022-12-28 18:30:33 + */ +@Slf4j +@Api(tags = "IM端,商品接口") +@RestController +@RequestMapping("/im/goods/goods") +public class GoodsImController { + + /** + * 商品 + */ + @Autowired + private GoodsService goodsService; + /** + * 商品SKU + */ + @Autowired + private GoodsSkuService goodsSkuService; + /** + * ES商品搜索 + */ + @Autowired + private EsGoodsSearchService goodsSearchService; + + @Autowired + private HotWordsService hotWordsService; + + @ApiOperation(value = "通过id获取商品信息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "skuId", value = "skuId", required = true, paramType = "path") + }) + @GetMapping(value = "/sku/{goodsId}/{skuId}") + @PageViewPoint(type = PageViewEnum.SKU, id = "#id") + public ResultMessage> getSku(@NotNull(message = "商品ID不能为空") @PathVariable("goodsId") String goodsId, + @NotNull(message = "SKU ID不能为空") @PathVariable("skuId") String skuId) { + try { + // 读取选中的列表 + Map map = goodsSkuService.getGoodsSkuDetail(goodsId, skuId); + return ResultUtil.data(map); + } catch (ServiceException se) { + log.info(se.getMsg(), se); + throw se; + } catch (Exception e) { + log.error(ResultCode.GOODS_ERROR.message(), e); + return ResultUtil.error(ResultCode.GOODS_ERROR); + } + + } + + +} diff --git a/im-api/src/main/java/cn/lili/controller/im/ImManagerController.java b/im-api/src/main/java/cn/lili/controller/im/ImManagerController.java new file mode 100644 index 00000000..794032ba --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/ImManagerController.java @@ -0,0 +1,175 @@ +package cn.lili.controller.im; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.security.token.Token; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dto.AdminUserDTO; +import cn.lili.modules.permission.entity.vo.AdminUserVO; +import cn.lili.modules.permission.service.AdminUserService; +import cn.lili.modules.permission.service.DepartmentService; +import cn.lili.mybatis.util.PageUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Slf4j +@RestController +@Api(tags = "管理员") +@RequestMapping("/im/manager/im/passport/user") +@Validated +public class ImManagerController { + @Autowired + private AdminUserService adminUserService; + @Autowired + private DepartmentService departmentService; + + @PostMapping(value = "/login") + @ApiOperation(value = "登录管理员") + public ResultMessage login(@NotNull(message = "用户名不能为空") String username, + @NotNull(message = "密码不能为空") String password) { + return ResultUtil.data(adminUserService.login(username, password)); + } + + @ApiOperation(value = "注销接口") + @PostMapping("/logout") + public ResultMessage logout() { + this.adminUserService.logout(UserEnums.MANAGER); + return ResultUtil.success(); + } + + @ApiOperation(value = "刷新token") + @GetMapping("/refresh/{refreshToken}") + public ResultMessage refreshToken(@NotNull(message = "刷新token不能为空") @PathVariable String refreshToken) { + return ResultUtil.data(this.adminUserService.refreshToken(refreshToken)); + } + + + @GetMapping(value = "/info") + @ApiOperation(value = "获取当前登录用户接口") + public ResultMessage getUserInfo() { + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + AdminUserVO adminUser = new AdminUserVO(adminUserService.findByUsername(tokenUser.getUsername())); + if (StringUtils.isNotEmpty(adminUser.getDepartmentId())) { + adminUser.setDepartmentTitle(departmentService.getById(adminUser.getDepartmentId()).getTitle()); + } + adminUser.setPassword(null); + return ResultUtil.data(adminUser); + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @PutMapping(value = "/edit") + @ApiOperation(value = "修改用户自己资料", notes = "用户名密码不会修改") + public ResultMessage editOwner(AdminUser adminUser) { + + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + //查询当前管理员 + AdminUser oldAdminUser = adminUserService.findByUsername(tokenUser.getUsername()); + oldAdminUser.setAvatar(adminUser.getAvatar()); + oldAdminUser.setNickName(adminUser.getNickName()); + if (!adminUserService.updateById(oldAdminUser)) { + throw new ServiceException(ResultCode.USER_EDIT_ERROR); + } + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @PutMapping(value = "/admin/edit") + @ApiOperation(value = "超级管理员修改其他管理员资料") + public ResultMessage edit(@Valid AdminUser adminUser, + @RequestParam(required = false) List roles) { + if (!adminUserService.updateAdminUser(adminUser, roles)) { + throw new ServiceException(ResultCode.USER_EDIT_ERROR); + } + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + /** + * 修改密码 + * + * @param password + * @param newPassword + * @return + */ + @PutMapping(value = "/editPassword") + @ApiOperation(value = "修改密码") + public ResultMessage editPassword(String password, String newPassword) { + adminUserService.editPassword(password, newPassword); + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + @PostMapping(value = "/resetPassword/{ids}") + @ApiOperation(value = "重置密码") + public ResultMessage resetPassword(@PathVariable List ids) { + adminUserService.resetPassword(ids); + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + @GetMapping + @ApiOperation(value = "多条件分页获取用户列表") + public ResultMessage> getByCondition(AdminUserDTO user, + SearchVO searchVo, + PageVO pageVo) { + IPage page = adminUserService.adminUserPage(PageUtil.initPage(pageVo), PageUtil.initWrapper(user, searchVo)); + + return ResultUtil.data(page); + } + + + @PostMapping + @ApiOperation(value = "添加用户") + public ResultMessage register(@Valid AdminUserDTO adminUser, + @RequestParam(required = false) List roles) { + int rolesMaxSize = 10; + try { + if (roles != null && roles.size() >= rolesMaxSize) { + throw new ServiceException(ResultCode.PERMISSION_BEYOND_TEN); + } + adminUserService.saveAdminUser(adminUser, roles); + } catch (Exception e) { + log.error("添加用户错误", e); + } + return ResultUtil.success(); + } + + @PutMapping(value = "/enable/{userId}") + @ApiOperation(value = "禁/启 用 用户") + public ResultMessage disable(@ApiParam("用户唯一id标识") @PathVariable String userId, Boolean status) { + AdminUser user = adminUserService.getById(userId); + if (user == null) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + user.setStatus(status); + adminUserService.updateById(user); + return ResultUtil.success(); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "批量通过ids删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + adminUserService.deleteCompletely(ids); + return ResultUtil.success(); + } + +} diff --git a/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java new file mode 100644 index 00000000..3aedb25d --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java @@ -0,0 +1,89 @@ +package cn.lili.controller.im; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.dos.ImMessage; +import cn.lili.modules.im.entity.dto.MessageQueryParams; +import cn.lili.modules.im.service.ImMessageService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * @author Chopper + */ +@RestController +@Api(tags = "Im消息接口") +@RequestMapping("/im/message") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ImMessageController { + + private final ImMessageService imMessageService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看Im消息详情") + public ResultMessage get(@PathVariable String id) { + ImMessage imMessage = imMessageService.getById(id); + return new ResultUtil().setData(imMessage); + } + + @GetMapping + @ApiOperation(value = "分页获取Im消息") + public ResultMessage> historyMessage(MessageQueryParams messageQueryParams) { + List data = imMessageService.getList(messageQueryParams); + return new ResultUtil>().setData(data); + } + + @PostMapping + @ApiOperation(value = "新增Im消息") + public ResultMessage save(ImMessage imMessage) { + if (imMessageService.save(imMessage)) { + return new ResultUtil().setData(imMessage); + } + return new ResultUtil().setErrorMsg(ResultCode.ERROR); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新Im消息") + public ResultMessage update(@PathVariable String id, ImMessage imMessage) { + if (imMessageService.updateById(imMessage)) { + return new ResultUtil().setData(imMessage); + } + return new ResultUtil().setErrorMsg(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除Im消息") + public ResultMessage delAllByIds(@PathVariable List ids) { + imMessageService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @GetMapping(value = "/newMessage") + @ApiOperation(value = "查看是否有新消息") + public ResultMessage hasNewMessage(String accessToken) { + return ResultUtil.data(imMessageService.hasNewMessage(accessToken)); + } + + @GetMapping(value = "/unredMessage") + @ApiOperation(value = "获取所有未读消息") + public ResultMessage getUnreadMessageCount() { + return ResultUtil.data(imMessageService.unreadMessageCount()); + } + + @PutMapping(value = "/clean/unred") + @ApiOperation(value = "清除所有未读消息") + public ResultMessage cleanUnreadMessage() { + imMessageService.cleanUnreadMessage(); + return ResultUtil.success(); + } +} diff --git a/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java b/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java new file mode 100644 index 00000000..4ed9576a --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java @@ -0,0 +1,88 @@ +package cn.lili.controller.im; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.modules.im.entity.vo.ImTalkVO; +import cn.lili.modules.im.service.ImTalkService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + + +/** + * @author Chopper + */ +@RestController +@Api(tags = "聊天接口") +@RequestMapping("/im/talk") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ImTalkController { + + private final ImTalkService imTalkService; + + @Autowired + private StoreService storeService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看聊天详情") + public ResultMessage get(@PathVariable String id) { + + ImTalk imTalk = imTalkService.getById(id); + return new ResultUtil().setData(imTalk); + } + + @GetMapping(value = "/user/{uid}") + @ApiOperation(value = "查看与某人聊天详情") + public ResultMessage getUser(@PathVariable String uid) { + AuthUser authUser = UserContext.getCurrentUser(); + return ResultUtil.data(imTalkService.getTalkByUser(authUser.getId(), uid)); + } + + @GetMapping(value = "/by/user/{userId}") + @ApiOperation(value = "查看与某人聊天详情") + public ResultMessage getByUser(@PathVariable String userId) { + AuthUser authUser = UserContext.getCurrentUser(); + return ResultUtil.data(new ImTalkVO(imTalkService.getTalkByUser(authUser.getId(), userId), authUser.getId())); + } + + @GetMapping(value = "/top") + @ApiOperation(value = "查看与某人聊天详情") + public ResultMessage top(String id, Boolean top) { + imTalkService.top(id, top); + return ResultUtil.success(); + } + + @GetMapping("/list") + @ApiOperation(value = "分页获取聊天") + @ApiImplicitParam(name = "userName", value = "用户名称", paramType = "query", dataType = "String") + public ResultMessage> getUserTalkList(String userName) { + return ResultUtil.data(imTalkService.getUserTalkList(userName)); + } + + @GetMapping("/store/list") + @ApiOperation(value = "分页获取商家聊天") + public ResultMessage> getStoreTalkList() { + return ResultUtil.data(imTalkService.getStoreTalkList()); + } + + @DeleteMapping(value = "/{id}") + @ApiOperation(value = "删除聊天") + public ResultMessage disable(@PathVariable String id) { + imTalkService.disable(id); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/im-api/src/main/java/cn/lili/controller/im/ImUserController.java b/im-api/src/main/java/cn/lili/controller/im/ImUserController.java new file mode 100644 index 00000000..b3b56937 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/ImUserController.java @@ -0,0 +1,81 @@ +package cn.lili.controller.im; + + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; +import cn.lili.modules.member.service.FootprintService; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +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; + + +/** + * @author Chopper + */ +@RestController +@Api(tags = "Im消息接口") +@RequestMapping("/im/user") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ImUserController { + + private final MemberService memberService; + + @Autowired + private StoreService storeService; + + @Autowired + private FootprintService footprintService; + + @GetMapping + @ApiOperation(value = "获取用户信息") + public ResultMessage getImUser() { + AuthUser authUser = UserContext.getCurrentUser(); + return ResultUtil.data(memberService.getById(authUser.getId())); + } + + @GetMapping("/store") + @ApiOperation(value = "获取店铺信息") + public ResultMessage getStoreUser() { + AuthUser authUser = UserContext.getCurrentUser(); + return ResultUtil.data(storeService.getById(authUser.getStoreId())); + } + + @GetMapping("/{memberId}") + @ApiImplicitParam(name = "memberId", value = "店铺Id", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "获取用户信息") + public ResultMessage getImUserDetail(@PathVariable String memberId) { + return ResultUtil.data(memberService.getById(memberId)); + } + + @GetMapping("/store/{storeId}") + @ApiImplicitParam(name = "storeId", value = "店铺Id", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "获取店铺信息") + public ResultMessage getStoreUserDetail(@PathVariable String storeId) { + return ResultUtil.data(storeService.getById(storeId)); + } + + @GetMapping("/history") + @ApiOperation(value = "获取会员的历史足迹") + public ResultMessage> getMemberHistory(FootPrintQueryParams params) { + return ResultUtil.data(footprintService.footPrintPage(params)); + } + +} diff --git a/im-api/src/main/java/cn/lili/controller/im/QAStoreController.java b/im-api/src/main/java/cn/lili/controller/im/QAStoreController.java new file mode 100644 index 00000000..b7be12dc --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/QAStoreController.java @@ -0,0 +1,62 @@ +package cn.lili.controller.im; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.dos.QA; +import cn.lili.modules.im.service.QAService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 管理端,自定义分词接口 + * + * @author paulG + * @since 2020/10/16 + **/ +@Slf4j +@RestController +@Api(tags = "管理端,自定义分词接口") +@RequestMapping("/im/store/qa") +public class QAStoreController { + + @Autowired + private QAService qaService; + + @ApiOperation(value = "添加问答") + @PostMapping + public ResultMessage addCustomWords(@Valid QA qa) { + qaService.save(qa); + return ResultUtil.data(qa); + } + + @ApiOperation(value = "修改自定义问答") + @PutMapping + public ResultMessage updateCustomWords(@Valid QA qa) { + qaService.updateById(qa); + return ResultUtil.data(qa); + } + + @ApiOperation(value = "删除自定义分词") + @DeleteMapping("/{id}") + public ResultMessage deleteCustomWords(@NotNull @PathVariable String id) { + qaService.removeById(id); + return ResultUtil.success(); + } + + @ApiOperation(value = "分页获取自定义分词") + @ApiImplicitParam(name = "word", value = "问题", required = true, dataType = "String", paramType = "query") + @GetMapping("/page") + public ResultMessage> getCustomWords(@RequestParam String word, PageVO pageVo) { + return ResultUtil.data(qaService.getStoreQA(word, pageVo)); + } + +} diff --git a/im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java b/im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java new file mode 100644 index 00000000..2b14e3a2 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java @@ -0,0 +1,188 @@ +package cn.lili.controller.im; + +import cn.lili.cache.Cache; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.im.config.CustomSpringConfigurator; +import cn.lili.modules.im.entity.dos.ImMessage; +import cn.lili.modules.im.entity.dos.ImTalk; +import cn.lili.modules.im.entity.enums.MessageResultType; +import cn.lili.modules.im.entity.vo.MessageOperation; +import cn.lili.modules.im.entity.vo.MessageVO; +import cn.lili.modules.im.service.ImMessageService; +import cn.lili.modules.im.service.ImTalkService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.service.StoreService; +import com.alibaba.druid.util.StringUtils; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author liushuai + */ +@Component +@ServerEndpoint(value = "/lili/webSocket/{accessToken}", configurator = CustomSpringConfigurator.class) +@Slf4j +public class WebSocketServer { + /** + * 消息服务 + */ + @Autowired + private ImMessageService imMessageService; + + /** + * im用户服务 + */ + @Autowired + private MemberService memberService; + + @Autowired + private StoreService storeService; + + @Autowired + private ImTalkService imTalkService; + + + @Autowired + private Cache cache; + /** + * 在线人数 + * PS 注意,只能单节点,如果多节点部署需要自行寻找方案 + */ + private static ConcurrentHashMap sessionPools = new ConcurrentHashMap<>(); + + /** + * 建立连接 + * + * @param session + */ + @OnOpen + public void onOpen(@PathParam("accessToken") String accessToken, Session session) throws IOException { + AuthUser authUser = UserContext.getAuthUser(accessToken); + Object message = null; + if (UserEnums.STORE.equals(authUser.getRole())) { + message = storeService.getById(authUser.getStoreId()); + sessionPools.put(authUser.getStoreId(), session); + + } else if (UserEnums.MEMBER.equals(authUser.getRole())) { + message = memberService.getById(authUser.getId()); + sessionPools.put(authUser.getId(), session); + } + MessageVO messageVO = new MessageVO(MessageResultType.FRIENDS, message); + sendMessage(authUser.getId(), messageVO); + } + + /** + * 关闭连接 + */ + @OnClose + public void onClose(@PathParam("accessToken") String accessToken) { + log.info("断开连接:{}", accessToken); + sessionPools.remove(UserContext.getAuthUser(accessToken).getId()); + } + + /** + * 发送消息 + * + * @param msg + * @throws IOException + */ + @OnMessage + public void onMessage(@PathParam("accessToken") String accessToken, String msg) { + log.error(msg); + MessageOperation messageOperation = JSON.parseObject(msg, MessageOperation.class); + operation(accessToken, messageOperation); + } + + /** + * IM操作 + * + * @param accessToken + * @param messageOperation + */ + private void operation(String accessToken, MessageOperation messageOperation) { + + AuthUser authUser = UserContext.getAuthUser(accessToken); + switch (messageOperation.getOperationType()) { + case PING: + break; + case MESSAGE: + //保存消息 + ImMessage imMessage = new ImMessage(messageOperation); + imMessageService.save(imMessage); + //修改最后消息信息 + imTalkService.update(new LambdaUpdateWrapper().eq(ImTalk::getId,messageOperation.getTalkId()).set(ImTalk::getLastTalkMessage,messageOperation.getContext()) + .set(ImTalk::getLastTalkTime,imMessage.getCreateTime()) + .set(ImTalk::getLastMessageType,imMessage.getMessageType())); + //发送消息 + sendMessage(messageOperation.getTo(), new MessageVO(MessageResultType.MESSAGE, imMessage)); + break; + case READ: + if (!StringUtils.isEmpty(messageOperation.getContext())) { + imMessageService.read(messageOperation.getTalkId(), accessToken); + } + break; + case UNREAD: + sendMessage(authUser.getId(), new MessageVO(MessageResultType.UN_READ, imMessageService.unReadMessages(accessToken))); + break; + case HISTORY: + sendMessage(authUser.getId(), new MessageVO(MessageResultType.HISTORY, imMessageService.historyMessage(accessToken, messageOperation.getTo()))); + break; + default: + break; + } + } + + /** + * 发送消息 + * + * @param key 密钥 + * @param message 消息对象 + */ + private void sendMessage(String key, MessageVO message) { + Session session = sessionPools.get(key); + if (session != null) { + try { + session.getBasicRemote().sendText(JSON.toJSONString(message, true)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * socket exception + * + * @param session + * @param throwable + */ + @OnError + public void onError(Session session, Throwable throwable) { + throwable.printStackTrace(); + } + + /** + * 获取店铺id + * + * @return + */ + private String storeKey(String storeId) { + return "STORE_" + storeId; + } + +} diff --git a/im-api/src/main/java/cn/lili/controller/orders/ImOrderComtroller.java b/im-api/src/main/java/cn/lili/controller/orders/ImOrderComtroller.java new file mode 100644 index 00000000..4b057f6c --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/orders/ImOrderComtroller.java @@ -0,0 +1,42 @@ +package cn.lili.controller.orders; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.dto.ImQueryParams; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.mybatis.util.PageUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * IM端,订单接口 + * + * @author chc + * @since 2022/6/2114:46 + */ +@Slf4j +@Api(tags = "IM端,订单接口") +@RestController +@RequestMapping("/im/orders/orders") +public class ImOrderComtroller { + + @Autowired + private OrderService orderService; + + @GetMapping("") + @ApiOperation(value = "获取会员订单列表") + public ResultMessage> getMemberHistory(OrderSearchParams params) { + return ResultUtil.data(orderService.queryByParams(params)); + } +} diff --git a/im-api/src/main/java/cn/lili/controller/seat/SeatLogin.java b/im-api/src/main/java/cn/lili/controller/seat/SeatLogin.java new file mode 100644 index 00000000..d6d59877 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/seat/SeatLogin.java @@ -0,0 +1,59 @@ +package cn.lili.controller.seat; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.service.SeatService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 坐席登录接口 + * + * @author Chopper + * @version v1.0 + * 2022-02-10 16:40 + */ +@Slf4j +@RestController +@Api(tags = "坐席端") +@RequestMapping("/seat/login") +public class SeatLogin { + + @Autowired + private SeatService seatService; + + @ApiOperation(value = "登录接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query") + }) + @PostMapping("/userLogin") + public ResultMessage userLogin(String username, String password) { + return ResultUtil.data(this.seatService.usernameLogin(username, password)); + } + + @ApiOperation(value = "商家快捷登录客服") + @PostMapping("/quicklogin") + public ResultMessage quickLogin(String code) { + return ResultUtil.data(this.seatService.quickLogin(code)); + } + + + @ApiOperation(value = "登出") + @PostMapping("/logout") + public ResultMessage logout() { + //todo +// UserContext.getCurrentUser().getId() +// verificationServiceClient.check(uuid); + return ResultUtil.success(); + } + + +} diff --git a/im-api/src/main/java/cn/lili/controller/seat/SeatSettingStoreController.java b/im-api/src/main/java/cn/lili/controller/seat/SeatSettingStoreController.java new file mode 100644 index 00000000..b5d6d69a --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/seat/SeatSettingStoreController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.seat; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.dos.SeatSetting; +import cn.lili.modules.im.service.SeatSettingService;; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 店铺端,分类绑定参数组管理接口 + * + * @author pikachu + * @since 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "店铺端,坐席设置") +@RequestMapping("/store/seat/setting") +@Transactional(rollbackFor = Exception.class) +public class SeatSettingStoreController { + + @Autowired + private SeatSettingService seatSettingService; + + @ApiOperation(value = "查询坐席设置") + @GetMapping + public ResultMessage getSetting() { + return ResultUtil.data(seatSettingService.getSetting(UserContext.getCurrentUser().getTenantId())); + } + + @ApiOperation(value = "更新坐席设置") + @PutMapping + public void update(SeatSetting seatSetting) { + seatSetting.setTenantId(UserContext.getCurrentUser().getTenantId()); + seatSettingService.updateByStore(seatSetting); + } +} diff --git a/im-api/src/main/java/cn/lili/controller/seat/SeatStoreController.java b/im-api/src/main/java/cn/lili/controller/seat/SeatStoreController.java new file mode 100644 index 00000000..e6b4552d --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/seat/SeatStoreController.java @@ -0,0 +1,42 @@ +package cn.lili.controller.seat; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.vo.SeatVO; +import cn.lili.modules.im.service.SeatService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * SeatController + * + * @author Chopper + * @version v1.0 + * 2022-02-10 11:50 + */ +@RestController +@Api(tags = "店铺端,坐席管理") +@RequestMapping("/store/seat/setting") +@Transactional(rollbackFor = Exception.class) +public class SeatStoreController { + + + @Autowired + private SeatService seatService; + + @ApiOperation(value = "分页获取坐席") + @GetMapping("/list") + public ResultMessage> getSeats() { + return ResultUtil.data(seatService.seatVoList(UserContext.getCurrentUser().getTenantId())); + } + + +} diff --git a/im-api/src/main/java/cn/lili/controller/seat/SeatStoreManagerController.java b/im-api/src/main/java/cn/lili/controller/seat/SeatStoreManagerController.java new file mode 100644 index 00000000..40f46b08 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/seat/SeatStoreManagerController.java @@ -0,0 +1,39 @@ +package cn.lili.controller.seat; + +import cn.lili.common.enums.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.im.entity.vo.SeatVO; +import cn.lili.modules.im.service.SeatService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * SeatController + * + * @author Chopper + * @version v1.0 + * 2022-02-10 11:50 + */ +@RestController +@Api(tags = "店铺端,坐席管理") +@RequestMapping("/manager/seat/setting") +@Transactional(rollbackFor = Exception.class) +public class SeatStoreManagerController { + + @Autowired + private SeatService seatService; + + @ApiOperation(value = "查看店铺坐席列表") + @GetMapping("/list") + public ResultMessage> getSeats(String storeId) { + return ResultUtil.data(seatService.seatVoList(storeId)); + } + +} diff --git a/im-api/src/main/java/cn/lili/controller/security/ImSecurityConfig.java b/im-api/src/main/java/cn/lili/controller/security/ImSecurityConfig.java new file mode 100644 index 00000000..185b3ad9 --- /dev/null +++ b/im-api/src/main/java/cn/lili/controller/security/ImSecurityConfig.java @@ -0,0 +1,53 @@ +package cn.lili.controller.security; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * spring Security 核心配置类 通用安全 + * + * @author Chopper + * @version v4.0 + * @since 2020/11/14 16:20 + */ +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class ImSecurityConfig extends WebSecurityConfigurerAdapter { + + + /** + * spring security -》 权限不足处理 + */ + @Autowired + private CorsConfigurationSource corsConfigurationSource; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + registry + .and() + //禁止网页iframe + .headers().frameOptions().disable() + .and() + .authorizeRequests() + //任何请求 + .anyRequest() + //需要身份认证 + .permitAll() + .and() + //允许跨域 + .cors().configurationSource(corsConfigurationSource).and() + //关闭跨站请求防护 + .csrf().disable(); + } + +} diff --git a/im-api/src/main/resources/application.yml b/im-api/src/main/resources/application.yml new file mode 100644 index 00000000..da5cd1a2 --- /dev/null +++ b/im-api/src/main/resources/application.yml @@ -0,0 +1,277 @@ +server: + port: 8885 + + servlet: + context-path: / + + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + application: + name: im-api + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + cache: + type: redis + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.mybatis.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.mybatis.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: false + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /manager/passport/user/login + - /manager/passport/user/refresh/** + - /manager/other/elasticsearch + - /manager/other/customWords + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + config: classpath:logback-spring.xml + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + file: + # 指定路径 + path: lili-logs + logback: + rollingpolicy: + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-file-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + # 脱敏级别: + # 0:不做脱敏处理 + # 1:管理端用户手机号等信息脱敏 + # 2:商家端信息脱敏(为2时,表示管理端,商家端同时脱敏) + sensitiveLevel: 1 + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc.b2b2c.pickmall.cn + wap: https://m.b2b2c.pickmall.cn + store: https://store.b2b2c.pickmall.cn + admin: https://admin.b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + logstash: + server: 127.0.0.1:4560 + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + store-topic: lili_store_topic + store-group: lili_store_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group + after-sale-topic: lili_after_sale_topic + after-sale-group: lili_after_sale_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/im-api/src/main/resources/logback-spring.xml b/im-api/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..b7c9425a --- /dev/null +++ b/im-api/src/main/resources/logback-spring.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + ${APP_NAME} + + + + ${LOG_FILE_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.log + 30 + + + ${FILE_LOG_PATTERN} + + + + + ${LOG_FILE_PATH}/rocketmq.log + + ${LOG_FILE_PATH}/rocketmq/rocketmq-%d{yyyy-MM-dd}.log + 30 + 30MB + + + %d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{ServiceId} - %m%n + + + + + + + + + + + ${LOGSTASH_SERVER} + + + + UTC + + + + {"appName":"${APP_NAME}"} + + + + + + + + \ No newline at end of file diff --git a/manager-api/src/main/resources/application.yml b/manager-api/src/main/resources/application.yml index 397323ae..fcb39e68 100644 --- a/manager-api/src/main/resources/application.yml +++ b/manager-api/src/main/resources/application.yml @@ -248,6 +248,8 @@ lili: order-group: lili_order_group member-topic: lili_member_topic member-group: lili_member_group + store-topic: lili_store_topic + store-group: lili_store_group other-topic: lili_other_topic other-group: lili_other_group notice-topic: lili_notice_topic diff --git a/pom.xml b/pom.xml index 839cd79b..fb8c22ca 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ common-api consumer admin + im-api diff --git a/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java b/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java index d08fdb51..3bbdd2c0 100644 --- a/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java +++ b/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java @@ -149,6 +149,16 @@ public class OrderStoreController { return ResultUtil.data(orderService.take(orderSn, verificationCode)); } + @PreventDuplicateSubmissions + @ApiOperation(value = "订单核验") + @ApiImplicitParams({ + @ApiImplicitParam(name = "verificationCode", value = "核验码", required = true, paramType = "path") + }) + @PutMapping(value = "/take/{verificationCode}") + public ResultMessage take(@PathVariable String verificationCode) { + return ResultUtil.data(orderService.take(verificationCode)); + } + @ApiOperation(value = "查询物流踪迹") @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") @GetMapping(value = "/getTraces/{orderSn}") diff --git a/seller-api/src/main/resources/application.yml b/seller-api/src/main/resources/application.yml index 7ba48b0d..eb93aaa3 100644 --- a/seller-api/src/main/resources/application.yml +++ b/seller-api/src/main/resources/application.yml @@ -241,6 +241,8 @@ lili: order-group: lili_order_group member-topic: lili_member_topic member-group: lili_member_group + store-topic: lili_store_topic + store-group: lili_store_group other-topic: lili_other_topic other-group: lili_other_group notice-topic: lili_notice_topic