Merge remote-tracking branch 'origin/master'

# Conflicts:
#	system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java
pull/1/head
sin 2019-05-18 10:35:09 +08:00
commit 1e3ea29dbb
127 changed files with 816 additions and 1191 deletions

View File

@ -30,19 +30,26 @@
# 演示 # 演示
> 艿艿:目前的开发者,都是后端出身。所以,一帮没有审美自觉的人,撸出来的前端界面,可能是东半球倒数第二难看。
>
> 迫切希望,有前端能力不错的小伙伴,加入我们,一起来完善「一个商城」。
>
> 啊啊啊!我好像做店铺装修功能。
## H5 商城 ## H5 商城
[体验传送门](http://h5.shop.iocoder.cn:18099) [体验传送门](http://h5.shop.iocoder.cn:18099)
TODO 此处应有一个演示的装逼 GIF 图。 ![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-h5-min.gif)
## 管理后台 ## 管理后台
[体验传送门](http://admin.shop.iocoder.cn:18099) [体验传送门](http://admin.shop.iocoder.cn:18099)
TODO 暂时不提供管理后台的账号密码,等后面提供。 * 账号yudaoyuanma
* 密码yudaoyuanma
TODO 此处应有一个演示的装逼 GIF 图。 ![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-admin-min.gif)
## 其它演示 ## 其它演示

View File

@ -35,4 +35,8 @@ public class StringUtil {
return org.apache.commons.lang3.StringUtils.substring(str, start); return org.apache.commons.lang3.StringUtils.substring(str, start);
} }
public static void main(String[] args) {
System.out.println(StringUtil.split("cn.iocoder.mall.order.api.OrderService#updatePaySuccess#1.0.0", "#").size());
}
} }

View File

@ -1 +0,0 @@
package cn.iocoder.mall.spring.boot;

View File

@ -2,6 +2,7 @@ package cn.iocoder.mall.spring.boot.web;
import cn.iocoder.common.framework.constant.MallConstants; import cn.iocoder.common.framework.constant.MallConstants;
import cn.iocoder.common.framework.servlet.CorsFilter; import cn.iocoder.common.framework.servlet.CorsFilter;
import cn.iocoder.mall.admin.sdk.interceptor.AdminDemoInterceptor;
import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor; import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor;
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor; import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler; import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler;
@ -34,6 +35,12 @@ public class AdminMVCAutoConfiguration implements WebMvcConfigurer {
return new AdminSecurityInterceptor(); return new AdminSecurityInterceptor();
} }
@Bean
@ConditionalOnMissingBean(AdminDemoInterceptor.class)
public AdminDemoInterceptor adminDemoInterceptor() {
return new AdminDemoInterceptor();
}
@Bean @Bean
@ConditionalOnMissingBean(GlobalResponseBodyHandler.class) @ConditionalOnMissingBean(GlobalResponseBodyHandler.class)
public GlobalResponseBodyHandler globalReturnValueHandler() { public GlobalResponseBodyHandler globalReturnValueHandler() {
@ -50,6 +57,7 @@ public class AdminMVCAutoConfiguration implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**"); registry.addInterceptor(adminAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
registry.addInterceptor(adminSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**"); registry.addInterceptor(adminSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
registry.addInterceptor(adminDemoInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
} }
@Bean @Bean

View File

@ -29,4 +29,6 @@
- 用户相关 - 用户相关
- [x] 登陆 - [x] 登陆
- [x] 注册 - [x] 注册
- [ ] 个人信息 - [x] 个人信息
- [ ] 手机改绑
- [ ] 微信登陆

View File

@ -10,31 +10,48 @@
- [ ] 支付单 20% 【待认领】 - [ ] 支付单 20% 【待认领】
- [ ] 退款单 20% 【待认领】 - [ ] 退款单 20% 【待认领】
- TODO 需要补充 - TODO 需要补充
- [ ] 店铺装修【迫切需要靠谱前端一起做】
- [ ] H5 装修
- [ ] 小程序装修
- [ ] 自定义页面
- [ ] 商品管理 - [ ] 商品管理
- [x] 发布商品 - [x] 发布商品
- [x] 商品列表 - [x] 商品列表
- [x] 展示类目 - [x] 展示类目
- [ ] 品牌管理【待认领】 - [ ] 品牌管理【开发中 @黑子】
- [ ] 商品标签
- [ ] 订单管理 - [ ] 订单管理
- [ ] 销售单 开发中 - [x] 销售单
- [ ] 售后单 开发中 - [x] 售后单
- [ ] 订单评价【开发中】 - [ ] 订单评价【开发中 @wang171776704
- [ ] 会员管理 - [ ] 会员管理
- [ ] 会员资料 20%【待认领】 - [ ] 会员资料 20%【待认领】
- [ ] 会员等级
- [ ] 会员积分
- [ ] 用户标签
- TODO 需要补充 - TODO 需要补充
- [ ] 营销管理 - [ ] 营销管理
- [x] 首页广告 - [x] 首页广告
- [x] 商品推荐 - [x] 商品推荐
- [x] 优惠劵 - [x] 优惠劵
- [ ] 优惠码【待认领 - [ ] 优惠码【开发中 @native8623 2019-05-17
- [ ] 满减送 20% 【待认领】 - [ ] 满减送 20% 【待认领】
- [ ] 限制折扣 20% 【待认领】 - [ ] 限制折扣 20% 【待认领】
- [ ] 多人拼团【待认领】 - [ ] 多人拼团【待认领】
- [ ] 积分商城
- [ ] 问卷调查
- [ ] 幸运大转盘
- [ ] 分销管理
- [ ] 分销设置
- [ ] 分销员管理
- [ ] 提现管理
- [ ] 系统管理 - [ ] 系统管理
- [x] 员工管理 - [x] 员工管理
- [x] 角色管理 <!--【前端页面需要细化下】--> - [x] 角色管理 <!--【前端页面需要细化下】-->
- [ ] 权限管理 - [x] 权限管理 <!--【前端页面需要细化下】-->
- [ ] 短信管理 - [ ] 部门管理【待认领】
- [x] 数据字典
- [ ] 短信管理【开发中 @小范】
- [ ] 短信模板 - [ ] 短信模板
- [ ] 发送日志 - [ ] 发送日志
- [ ] 员工操作日志 - [ ] 员工操作日志

View File

@ -229,11 +229,11 @@ service.interceptors.response.use(
// TODO token 过期 // TODO token 过期
// TODO 需要拿 refresh token 置换 // TODO 需要拿 refresh token 置换
if (code === 1001001011 // 访问令牌不存在 if (code === 1002001011 // 访问令牌不存在
|| code === 1001001013 // 访问令牌已失效 || code === 1002001013 // 访问令牌已失效
|| code === 1001001021 // 刷新令牌不存在 || code === 1002001017 // 刷新令牌不存在
|| code === 1001001022 // 刷新令牌已过期 || code === 1002001018 // 刷新令牌已过期
|| code === 1001001023) { // 刷新令牌已失效 || code === 1002001019) { // 刷新令牌已失效
Dialog.confirm({ Dialog.confirm({
title: '', title: '',
message: res.message, message: res.message,
@ -249,7 +249,7 @@ service.interceptors.response.use(
} }
} }
}); });
} else if (code === 1001001012) { // 访问令牌已过期 } else if (code === 1002001012) { // 访问令牌已过期
return refreshToken(response); return refreshToken(response);
} else { } else {
Dialog.alert({ Dialog.alert({

View File

@ -69,7 +69,7 @@ export default {
let that = this; let that = this;
let response = doPassportMobileRegister(this.mobile, this.code); let response = doPassportMobileRegister(this.mobile, this.code);
response.then(data => { response.then(data => {
setLoginToken(data.accessToken, data.refreshToken); setLoginToken(data.token.accessToken, data.token.refreshToken);
Dialog.alert({ Dialog.alert({
title: '系统提示', title: '系统提示',
message: '登陆成功', message: '登陆成功',

View File

@ -50,21 +50,6 @@
<div class="category-div"> <div class="category-div">
<!--<h4>热门分类</h4>--> <!--<h4>热门分类</h4>-->
<ul> <ul>
<!--<li><a ><img src="//img11.360buyimg.com/focus/s140x140_jfs/t21388/146/237407622/26923/221da1b3/5b054fedN2ba90518.jpg"><span>手机</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t20128/208/216721929/9242/472993da/5b05522dNa2aae1bb.png"><span>耳机</span></a></li>-->
<!--<li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t21655/83/2186874549/15932/c273d29b/5b48802aN13fe73de.png"><span>剃须刀</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t21715/149/246679831/16257/ddbf2036/5b0565a7N8dbc0017.png"><span>路由器</span></a></li>-->
<!--<li><a ><img src="//img14.360buyimg.com/focus/s140x140_jfs/t1/4478/16/633/36008/5b923503E39b9dfa9/13b099f187576d8c.png"><span>月饼</span></a></li>-->
<!--<li><a ><img src="//img10.360buyimg.com/focus/s140x140_jfs/t1/1410/32/643/38009/5b9236b2Eb02fbf02/1e7de6987578dcdd.jpg" ><span>牛奶</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t1/4674/14/665/25245/5b9236bbE088d5efb/6c7c2f9857736c65.jpg"><span>男士内裤</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t1/1710/26/666/26147/5b9236c3E5fd1cd42/86c6bca8f4fe1efa.png"><span>小米8</span></a></li>-->
<!--<li><a ><img src="//img11.360buyimg.com/focus/s140x140_jfs/t1/3653/6/655/42593/5b9236caEfef6235b/9e118f12705f52bb.png"><span>大闸蟹</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t23881/349/2204372862/9923/4c62864a/5b7693eeNf6883734.png"><span>三只松鼠</span></a></li>-->
<!--<li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t24253/294/2182777138/4059/429945c9/5b76990bNde226fbc.png"><span>充电宝</span></a></li>-->
<!--<li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t22051/318/235303191/9297/c5ea2761/5b055000N410a7553.png"><span>空调</span></a></li>-->
<!--<li><a ><img src="//img10.360buyimg.com/focus/s140x140_jfs/t19960/243/653029866/38879/91bb398b/5b055555N9245f8aa.jpg"><span>电饭煲</span></a></li>-->
<!--<li><a ><img src="//img12.360buyimg.com/focus/s140x140_jfs/t1/345/33/944/5582/5b9236d2E62d8da2e/99f72d51b8f195ed.jpg"><span>电话手表</span></a></li>-->
<!--<li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t1/1446/14/631/8500/5b9237e5E0d1f9e16/b1a627b92323b5ed.png"><span>华为</span></a></li>-->
<li v-for="category in childCategories"> <li v-for="category in childCategories">
<router-link :to="'/products/list?title=' + activeCategory.name + '&cidFirst=' + activeCategory.id + '&cidSecond=' + category.id"> <router-link :to="'/products/list?title=' + activeCategory.name + '&cidFirst=' + activeCategory.id + '&cidSecond=' + category.id">
<img :src="category.picUrl" /> <img :src="category.picUrl" />

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<headerNav title="个人信息"/> <headerNav title="个人信息"/>
<van-cell-group> <van-cell-group title="基础资料">
<!--<van-cell title="修改个人信息" is-link />--> <!--<van-cell title="修改个人信息" is-link />-->
<!--<van-cell title="修改登录密码" is-link />--> <!--<van-cell title="修改登录密码" is-link />-->
<!--<van-cell title="修改绑定手机" is-link />--> <!--<van-cell title="修改绑定手机" is-link />-->
@ -14,6 +14,10 @@
</van-cell-group> </van-cell-group>
<van-cell-group title="密保资料">
<van-cell title="手机号" :value="user.mobile" />
</van-cell-group>
<!-- 昵称修改弹出 --> <!-- 昵称修改弹出 -->
<van-dialog <van-dialog
v-model="showNicknameDialog" v-model="showNicknameDialog"

View File

@ -9,7 +9,6 @@ import cn.iocoder.mall.order.application.convert.OrderReturnConvert;
import cn.iocoder.mall.order.application.po.admin.OrderReturnQueryPO; import cn.iocoder.mall.order.application.po.admin.OrderReturnQueryPO;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -26,8 +25,7 @@ import javax.servlet.http.HttpServletRequest;
@Api("订单退货(admins api)") @Api("订单退货(admins api)")
public class AdminOrderReturnController { public class AdminOrderReturnController {
@Autowired @Reference(validation = "true", version = "${dubbo.provider.OrderReturnService.version}")
@Reference(validation = "true")
private OrderReturnService orderReturnService; private OrderReturnService orderReturnService;
@GetMapping("list") @GetMapping("list")

View File

@ -5,7 +5,9 @@ import cn.iocoder.mall.order.api.OrderService;
import cn.iocoder.mall.order.api.bo.OrderItemBO; import cn.iocoder.mall.order.api.bo.OrderItemBO;
import cn.iocoder.mall.order.api.bo.OrderPageBO; import cn.iocoder.mall.order.api.bo.OrderPageBO;
import cn.iocoder.mall.order.api.bo.OrderRecipientBO; import cn.iocoder.mall.order.api.bo.OrderRecipientBO;
import cn.iocoder.mall.order.api.dto.*; import cn.iocoder.mall.order.api.dto.OrderItemUpdateDTO;
import cn.iocoder.mall.order.api.dto.OrderLogisticsUpdateDTO;
import cn.iocoder.mall.order.api.dto.OrderQueryDTO;
import cn.iocoder.mall.order.application.convert.OrderConvertAPP; import cn.iocoder.mall.order.application.convert.OrderConvertAPP;
import cn.iocoder.mall.order.application.convert.OrderDeliveryConvert; import cn.iocoder.mall.order.application.convert.OrderDeliveryConvert;
import cn.iocoder.mall.order.application.po.admin.OrderDeliverPO; import cn.iocoder.mall.order.application.po.admin.OrderDeliverPO;
@ -15,7 +17,6 @@ import cn.iocoder.mall.order.application.po.admin.OrderPageQueryPO;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -29,10 +30,10 @@ import java.util.List;
*/ */
@RestController @RestController
@RequestMapping("admins/order") @RequestMapping("admins/order")
@Api(value = "订单API(admins)") @Api(value = "订单 API(admins)")
public class AdminsOrderController { public class AdminsOrderController {
@Reference(validation = "true") @Reference(validation = "true", version = "${dubbo.provider.OrderService.version}")
private OrderService orderService; private OrderService orderService;
@GetMapping("page") @GetMapping("page")

View File

@ -19,6 +19,7 @@ import cn.iocoder.mall.order.application.po.user.OrderCreatePO;
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO; import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
import cn.iocoder.mall.promotion.api.CouponService; import cn.iocoder.mall.promotion.api.CouponService;
import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO; import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO;
import cn.iocoder.mall.user.sdk.annotation.RequiresLogin;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -41,19 +42,23 @@ import static cn.iocoder.common.framework.vo.CommonResult.success;
*/ */
@RestController @RestController
@RequestMapping("users/order") @RequestMapping("users/order")
@Api(description = "用户订单") @Api(description = "用户订单") // TODO FROM 芋艿 to 小范description 已经废弃啦
public class OrderController { public class OrderController {
@Reference(validation = "true") @Reference(validation = "true", version = "${dubbo.provider.OrderReturnService.version}")
private OrderService orderService; private OrderService orderService;
@Reference(validation = "true", version = "${dubbo.provider.CartService.version}") @Reference(validation = "true", version = "${dubbo.provider.CartService.version}")
private CartService cartService; private CartService cartService;
@Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}") @Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}")
private DataDictService dataDictService; private DataDictService dataDictService;
@Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}") @Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}")
private CouponService couponService; private CouponService couponService;
@GetMapping("order_page") @GetMapping("order_page")
@RequiresLogin
@ApiOperation("订单分页") @ApiOperation("订单分页")
public CommonResult<OrderPageBO> getOrderPage(@Validated OrderQueryDTO orderQueryDTO) { public CommonResult<OrderPageBO> getOrderPage(@Validated OrderQueryDTO orderQueryDTO) {
Integer userId = UserSecurityContextHolder.getContext().getUserId(); Integer userId = UserSecurityContextHolder.getContext().getUserId();
@ -62,6 +67,7 @@ public class OrderController {
} }
@PostMapping("create_order") @PostMapping("create_order")
@RequiresLogin
@ApiOperation("创建订单") @ApiOperation("创建订单")
public CommonResult<OrderCreateBO> createOrder(@RequestBody @Validated OrderCreatePO orderCreatePO, public CommonResult<OrderCreateBO> createOrder(@RequestBody @Validated OrderCreatePO orderCreatePO,
HttpServletRequest request) { HttpServletRequest request) {
@ -72,6 +78,7 @@ public class OrderController {
} }
@PostMapping("create_order_from_cart") @PostMapping("create_order_from_cart")
@RequiresLogin
@ApiOperation("创建订单购物车") @ApiOperation("创建订单购物车")
public CommonResult<OrderCreateBO> createOrderFromCart(@RequestParam("userAddressId") Integer userAddressId, public CommonResult<OrderCreateBO> createOrderFromCart(@RequestParam("userAddressId") Integer userAddressId,
@RequestParam(value = "couponCardId", required = false) Integer couponCardId, @RequestParam(value = "couponCardId", required = false) Integer couponCardId,
@ -99,6 +106,7 @@ public class OrderController {
} }
@GetMapping("confirm_create_order") @GetMapping("confirm_create_order")
@RequiresLogin
@ApiOperation("确认创建订单") @ApiOperation("确认创建订单")
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId, public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity, @RequestParam("quantity") Integer quantity,
@ -118,6 +126,7 @@ public class OrderController {
} }
@PostMapping("confirm_receiving") @PostMapping("confirm_receiving")
@RequiresLogin
@ApiOperation("确认收货") @ApiOperation("确认收货")
public CommonResult confirmReceiving(@RequestParam("orderId") Integer orderId) { public CommonResult confirmReceiving(@RequestParam("orderId") Integer orderId) {
Integer userId = UserSecurityContextHolder.getContext().getUserId(); Integer userId = UserSecurityContextHolder.getContext().getUserId();
@ -125,6 +134,7 @@ public class OrderController {
} }
@GetMapping("info") @GetMapping("info")
@RequiresLogin
@ApiOperation("订单详情") @ApiOperation("订单详情")
public CommonResult<OrderInfoBO> orderInfo(@RequestParam("orderId") Integer orderId) { public CommonResult<OrderInfoBO> orderInfo(@RequestParam("orderId") Integer orderId) {
Integer userId = UserSecurityContextHolder.getContext().getUserId(); Integer userId = UserSecurityContextHolder.getContext().getUserId();

View File

@ -35,8 +35,9 @@ import java.util.stream.Collectors;
@Api(description = "订单物流信息") @Api(description = "订单物流信息")
public class OrderLogisticsController { public class OrderLogisticsController {
@Reference(validation = "true") @Reference(validation = "true", version = "${dubbo.provider.OrderLogisticsService.version}")
private OrderLogisticsService orderLogisticsService; private OrderLogisticsService orderLogisticsService;
@Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}") @Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}")
private DataDictService dataDictService; private DataDictService dataDictService;

View File

@ -25,8 +25,9 @@ import java.util.List;
@RequestMapping("users/order_return") @RequestMapping("users/order_return")
public class OrderReturnController { public class OrderReturnController {
@Reference(validation = "true") @Reference(validation = "true", version = "${dubbo.provider.OrderReturnService.version}")
private OrderReturnService orderReturnService; private OrderReturnService orderReturnService;
@Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}") @Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}")
private DataDictService dataDictService; private DataDictService dataDictService;

View File

@ -13,7 +13,6 @@ import cn.iocoder.mall.order.application.vo.UsersCartDetailVO;
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO; import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
import cn.iocoder.mall.promotion.api.CouponService; import cn.iocoder.mall.promotion.api.CouponService;
import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO; import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -31,8 +30,10 @@ public class UsersCartController {
@Reference(validation = "true", version = "${dubbo.provider.CartService.version}") @Reference(validation = "true", version = "${dubbo.provider.CartService.version}")
private CartService cartService; private CartService cartService;
@Reference(validation = "true")
@Reference(validation = "true", version = "${dubbo.provider.OrderService.version}")
private OrderService orderService; private OrderService orderService;
@Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}") @Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}")
private CouponService couponService; private CouponService couponService;
@ -125,7 +126,6 @@ public class UsersCartController {
} }
@GetMapping("/calc_sku_price") @GetMapping("/calc_sku_price")
@PermitAll
public CommonResult<UsersCalcSkuPriceVO> calcSkuPrice(@RequestParam("skuId") Integer skuId) { public CommonResult<UsersCalcSkuPriceVO> calcSkuPrice(@RequestParam("skuId") Integer skuId) {
// 计算 sku 的价格 // 计算 sku 的价格
CalcSkuPriceBO calcSkuPrice = cartService.calcSkuPrice(skuId); CalcSkuPriceBO calcSkuPrice = cartService.calcSkuPrice(skuId);

View File

@ -127,7 +127,7 @@ public interface OrderService {
CommonResult updateLogistics(OrderLogisticsUpdateDTO orderLogisticsDTO); CommonResult updateLogistics(OrderLogisticsUpdateDTO orderLogisticsDTO);
/** /**
* * // TODO FROM 芋艿 to 小范。删除订单,不要使用 deleted 字段,对于用户是删除,实际是隐藏。
* *
* @param id * @param id
*/ */

View File

@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class OrderRecipientBO extends BaseDO { public class OrderRecipientBO extends BaseDO { // TODO FROM 芋艿 TO 小范,不要继承 BaseDO
/** /**
* *

View File

@ -15,10 +15,12 @@ public class CalcOrderPriceDTO {
@NotNull(message = "用户编号不能为空") @NotNull(message = "用户编号不能为空")
private Integer userId; private Integer userId;
/** /**
* *
*/ */
private Integer couponCardId; private Integer couponCardId;
@NotNull(message = "商品数组不能为空") @NotNull(message = "商品数组不能为空")
private List<Item> items; private List<Item> items;

View File

@ -1,7 +0,0 @@
/**
* api
*
* @author Sin
* @time 2019-03-16 13:15
*/
package cn.iocoder.mall.order.api;

View File

@ -16,5 +16,5 @@ public class ServiceExceptionConfiguration {
// } catch (IOException e) { // } catch (IOException e) {
// throw new RuntimeException(e); // throw new RuntimeException(e);
// } // }
} } // TODO FROM 芋艿 to 小范,这里记得配置下,不然错误提示不出去呀。
} }

View File

@ -1,7 +0,0 @@
/**
*
*
* @author Sin
* @time 2019-03-20 21:16
*/
package cn.iocoder.mall.order.biz.constants;

View File

@ -18,7 +18,7 @@ import lombok.experimental.Accessors;
public class OrderCommentDO extends BaseDO { public class OrderCommentDO extends BaseDO {
/** /**
* id * id // TODO FROM 芋艿 TO wtz 中英文之间,要有空格
*/ */
private Integer id; private Integer id;
@ -103,7 +103,7 @@ public class OrderCommentDO extends BaseDO {
private Integer replayCount; private Integer replayCount;
/** /**
* * // TODO FROM 芋艿 TO wtz collect 是收藏的意思,最好换个单词噢。
*/ */
private Integer collectCount; private Integer collectCount;

View File

@ -8,6 +8,8 @@ import lombok.experimental.Accessors;
/** /**
* *
* *
* // TODO FROM 芋艿 TO wtz 商品评价回复表 =》订单评论回复表
*
* @author wtz * @author wtz
* @time 2019-05-14 21:00 * @time 2019-05-14 21:00
* *
@ -28,7 +30,7 @@ public class OrderCommentReplayDO extends BaseDO {
private Integer commentId; private Integer commentId;
/** /**
* * // TODO FROM 芋艿 TO wtz 记得加下枚举类
*/ */
private Integer replyType; private Integer replyType;
@ -73,7 +75,7 @@ public class OrderCommentReplayDO extends BaseDO {
private String replyUserAvatar; private String replyUserAvatar;
/** /**
* * // TODO FROM 芋艿 TO wtz 【提示】userType 和 UserTypeEnum 记录保持一致。
*/ */
private Integer replyUserType; private Integer replyUserType;

View File

@ -1,7 +1,6 @@
package cn.iocoder.mall.order.biz.dataobject; package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO; import cn.iocoder.common.framework.dataobject.BaseDO;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -17,6 +16,8 @@ import java.util.Date;
@Accessors(chain = true) @Accessors(chain = true)
public class OrderReturnDO extends BaseDO { public class OrderReturnDO extends BaseDO {
// TODO FROM 芋艿 TO 小范,存储下支付中心的退款单号
/** /**
* *
*/ */
@ -24,6 +25,7 @@ public class OrderReturnDO extends BaseDO {
/** /**
* *
*/ */
// TODO FROM 芋艿 to 小范,换个名字,看着怪怪的 哈哈哈哈。
private String serviceNumber; private String serviceNumber;
/** /**
* *
@ -54,6 +56,7 @@ public class OrderReturnDO extends BaseDO {
/** /**
* *
*/ */
// TODO FROM 芋艿 to 小范describe 是动词,换成名词 description
private String describe; private String describe;
/// ///

View File

@ -21,7 +21,7 @@ import cn.iocoder.mall.order.biz.dataobject.OrderDO;
import cn.iocoder.mall.order.biz.dataobject.OrderItemDO; import cn.iocoder.mall.order.biz.dataobject.OrderItemDO;
import cn.iocoder.mall.order.biz.dataobject.OrderReturnDO; import cn.iocoder.mall.order.biz.dataobject.OrderReturnDO;
import cn.iocoder.mall.pay.api.PayRefundService; import cn.iocoder.mall.pay.api.PayRefundService;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundSubmitDTO;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@ -14,8 +14,8 @@ import cn.iocoder.mall.order.biz.convert.*;
import cn.iocoder.mall.order.biz.dao.*; import cn.iocoder.mall.order.biz.dao.*;
import cn.iocoder.mall.order.biz.dataobject.*; import cn.iocoder.mall.order.biz.dataobject.*;
import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.PayTransactionService;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionCreateDTO;
import cn.iocoder.mall.product.api.ProductSpuService; import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO; import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import cn.iocoder.mall.promotion.api.CouponService; import cn.iocoder.mall.promotion.api.CouponService;
@ -79,7 +79,7 @@ public class OrderServiceImpl implements OrderService {
public CommonResult<OrderPageBO> getOrderPage(OrderQueryDTO orderQueryDTO) { public CommonResult<OrderPageBO> getOrderPage(OrderQueryDTO orderQueryDTO) {
int totalCount = orderMapper.selectPageCount(orderQueryDTO); int totalCount = orderMapper.selectPageCount(orderQueryDTO);
if (totalCount == 0) { if (totalCount == 0) { // TODO FROM 芋艿 TO 小范 Collections.EMPTY_LIST 改成 Collections.emptyList()
return CommonResult.success(new OrderPageBO().setOrders(Collections.EMPTY_LIST).setTotal(0)); return CommonResult.success(new OrderPageBO().setOrders(Collections.EMPTY_LIST).setTotal(0));
} }
@ -92,7 +92,7 @@ public class OrderServiceImpl implements OrderService {
// 获取订单 id // 获取订单 id
Set<Integer> orderIds = orderDOList.stream() Set<Integer> orderIds = orderDOList.stream()
.map(orderDO -> orderDO.getId()) .map(orderDO -> orderDO.getId()) // TODO FROM 芋艿 to 小范,记得用 Lambda
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// 获取配送信息 // 获取配送信息
@ -231,10 +231,10 @@ public class OrderServiceImpl implements OrderService {
// 设置 orderItem // 设置 orderItem
Map<Integer, ProductSkuDetailBO> productSpuBOMap = productList Map<Integer, ProductSkuDetailBO> productSpuBOMap = productList
.stream().collect(Collectors.toMap(ProductSkuDetailBO::getId, o -> o)); // 商品 SKU 信息的集合 .stream().collect(Collectors.toMap(ProductSkuDetailBO::getId, o -> o)); // 商品 SKU 信息的集合
Map<Integer, CalcOrderPriceBO.Item> priceItemMap = new HashMap<>(); Map<Integer, CalcOrderPriceBO.Item> priceItemMap = new HashMap<>(); // 商品 SKU 价格的映射
calcOrderPrice.getItemGroups().forEach(itemGroup -> calcOrderPrice.getItemGroups().forEach(itemGroup ->
itemGroup.getItems().forEach(item -> priceItemMap.put(item.getId(), item))); itemGroup.getItems().forEach(item -> priceItemMap.put(item.getId(), item)));
// 遍历 orderItemDOList 数组,将商品信息、商品价格,设置到其中
for (OrderItemDO orderItemDO : orderItemDOList) { for (OrderItemDO orderItemDO : orderItemDOList) {
ProductSkuDetailBO productSkuDetailBO = productSpuBOMap.get(orderItemDO.getSkuId()); ProductSkuDetailBO productSkuDetailBO = productSpuBOMap.get(orderItemDO.getSkuId());
if (productSkuDetailBO.getQuantity() <= 0) { if (productSkuDetailBO.getQuantity() <= 0) {
@ -267,6 +267,7 @@ public class OrderServiceImpl implements OrderService {
// order // order
// TODO: 2019-04-11 Sin 订单号需要生成规则 // TODO: 2019-04-11 Sin 订单号需要生成规则
// TODO FROM 芋艿 to 小范:可以考虑抽象成一个方法,下面几个也是。
String orderNo = UUID.randomUUID().toString().replace("-", "").substring(0, 16); String orderNo = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
// Integer totalAmount = orderCommon.calculatedAmount(orderItemDOList); // Integer totalAmount = orderCommon.calculatedAmount(orderItemDOList);
// Integer totalPrice = orderCommon.calculatedPrice(orderItemDOList); // Integer totalPrice = orderCommon.calculatedPrice(orderItemDOList);
@ -323,10 +324,6 @@ public class OrderServiceImpl implements OrderService {
// 一次性插入 // 一次性插入
orderItemMapper.insert(orderItemDOList); orderItemMapper.insert(orderItemDOList);
if (true) {
throw new RuntimeException("测试 seata 事务回滚");
}
// 创建预订单 // 创建预订单
createPayTransaction(orderDO, orderItemDOList, orderCreateDTO.getIp()); createPayTransaction(orderDO, orderItemDOList, orderCreateDTO.getIp());
@ -358,7 +355,7 @@ public class OrderServiceImpl implements OrderService {
return cartService.calcOrderPrice(calcOrderPriceDTO); return cartService.calcOrderPrice(calcOrderPriceDTO);
} }
private CommonResult<PayTransactionBO> createPayTransaction(OrderDO order, List<OrderItemDO> orderItems, String ip) { private PayTransactionBO createPayTransaction(OrderDO order, List<OrderItemDO> orderItems, String ip) {
// TODO sin 支付订单 orderSubject 暂时取第一个子订单商品信息 // TODO sin 支付订单 orderSubject 暂时取第一个子订单商品信息
String orderSubject = orderItems.get(0).getSkuName(); String orderSubject = orderItems.get(0).getSkuName();
Date expireTime = DateUtil.addDate(Calendar.MINUTE, PAY_EXPIRE_TIME); Date expireTime = DateUtil.addDate(Calendar.MINUTE, PAY_EXPIRE_TIME);
@ -441,6 +438,7 @@ public class OrderServiceImpl implements OrderService {
.setUpdateTime(null); .setUpdateTime(null);
// 关闭订单,修改状态 item // 关闭订单,修改状态 item
// TODO FROM 芋艿 TO 小范更新的时候where 里面带下 status 避免并发的问题
orderItemMapper.updateByOrderId( orderItemMapper.updateByOrderId(
orderId, orderId,
new OrderItemDO().setStatus(OrderStatusEnum.CLOSED.getValue()) new OrderItemDO().setStatus(OrderStatusEnum.CLOSED.getValue())
@ -454,18 +452,18 @@ public class OrderServiceImpl implements OrderService {
} }
@Override @Override
@Transactional @Transactional // TODO FROM 芋艿 TO 小范:泛型,一定要明确哈。
public CommonResult orderDelivery(OrderDeliveryDTO orderDelivery) { public CommonResult orderDelivery(OrderDeliveryDTO orderDelivery) {
List<Integer> orderItemIds = orderDelivery.getOrderItemIds(); List<Integer> orderItemIds = orderDelivery.getOrderItemIds();
// 获取所有订单 items // 获取所有订单 items // TODO FROM 芋艿 TO 小范deleted 是默认条件,所以 by 里面可以不带哈
List<OrderItemDO> allOrderItems = orderItemMapper.selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue()); List<OrderItemDO> allOrderItems = orderItemMapper.selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue());
// 当前需要发货订单,检查 id 和 status // 当前需要发货订单,检查 id 和 status
List<OrderItemDO> needDeliveryOrderItems = allOrderItems.stream() List<OrderItemDO> needDeliveryOrderItems = allOrderItems.stream()
.filter(orderItemDO -> orderItemIds.contains(orderItemDO.getId()) .filter(orderItemDO -> orderItemIds.contains(orderItemDO.getId())
&& OrderStatusEnum.WAIT_SHIPMENT.getValue() == orderItemDO.getStatus()) && OrderStatusEnum.WAIT_SHIPMENT.getValue() == orderItemDO.getStatus())
.collect(Collectors.toList()); .collect(Collectors.toList()); // TODO 芋艿,如果这里只是比对数字,可以用 Lambda 求和,不需要弄成一个集合的
// 发货订单,检查 // 发货订单,检查
if (needDeliveryOrderItems.size() != orderItemIds.size()) { if (needDeliveryOrderItems.size() != orderItemIds.size()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_DELIVERY_INCORRECT_DATA.getCode()); return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_DELIVERY_INCORRECT_DATA.getCode());
@ -482,6 +480,7 @@ public class OrderServiceImpl implements OrderService {
orderLogisticsMapper.insert(orderLogisticsDO); orderLogisticsMapper.insert(orderLogisticsDO);
// 关联订单item 和 物流信息 // 关联订单item 和 物流信息
// TODO FROM 芋艿 TO 小范更新的时候where 里面带下 status 避免并发的问题,然后判断下更新数量,不对,就抛出异常。
orderItemMapper.updateByIds( orderItemMapper.updateByIds(
orderItemIds, orderItemIds,
new OrderItemDO() new OrderItemDO()
@ -495,6 +494,7 @@ public class OrderServiceImpl implements OrderService {
&& !orderItemIds.contains(orderItemDO.getId())) && !orderItemIds.contains(orderItemDO.getId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (unShippedOrderItems.size() <= 0) { if (unShippedOrderItems.size() <= 0) {
// TODO FROM 芋艿 TO 小范更新的时候where 里面带下 status 避免并发的问题
orderMapper.updateById( orderMapper.updateById(
new OrderDO() new OrderDO()
.setId(orderDelivery.getOrderId()) .setId(orderDelivery.getOrderId())
@ -513,7 +513,7 @@ public class OrderServiceImpl implements OrderService {
} }
@Override @Override
@Transactional @Transactional // TODO FROM 芋艿 to 小范,先不做这个功能,电商一班不存在这个功能哈。
public CommonResult deleteOrderItem(OrderItemDeletedDTO orderItemDeletedDTO) { public CommonResult deleteOrderItem(OrderItemDeletedDTO orderItemDeletedDTO) {
Integer orderId = orderItemDeletedDTO.getOrderId(); Integer orderId = orderItemDeletedDTO.getOrderId();
List<Integer> orderItemIds = orderItemDeletedDTO.getOrderItemIds(); List<Integer> orderItemIds = orderItemDeletedDTO.getOrderItemIds();
@ -562,6 +562,7 @@ public class OrderServiceImpl implements OrderService {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_UNABLE_CONFIRM_ORDER.getCode()); return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_UNABLE_CONFIRM_ORDER.getCode());
} }
// TODO FROM 芋艿 TO 小范更新的时候where 里面带下 status 避免并发的问题
orderMapper.updateById( orderMapper.updateById(
new OrderDO() new OrderDO()
.setId(orderId) .setId(orderId)
@ -617,7 +618,7 @@ public class OrderServiceImpl implements OrderService {
if (updateCount <= 0) { if (updateCount <= 0) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_STATUS_NOT_WAITING_PAYMENT.getCode()).getMessage(); return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_STATUS_NOT_WAITING_PAYMENT.getCode()).getMessage();
} }
// TODO 芋艿 更新 OrderItemDO // TODO FROM 芋艿 to 小范,把更新 OrderItem 给补全。
return "success"; return "success";
} }

View File

@ -21,7 +21,6 @@ mybatis-plus:
id-type: auto id-type: auto
mapper-locations: classpath*:mapper/*.xml mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.order.biz.dataobject type-aliases-package: cn.iocoder.mall.order.biz.dataobject
config-location: classpath:mybatis-config.xml
# dubbo # dubbo
dubbo: dubbo:

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 使用驼峰命名法转换字段。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
</typeAliases>
</configuration>

View File

@ -3,10 +3,10 @@ package cn.iocoder.mall.pay.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.PayRefundService; import cn.iocoder.mall.pay.api.PayRefundService;
import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.PayTransactionService;
import cn.iocoder.mall.pay.api.bo.PayRefundBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundBO;
import cn.iocoder.mall.pay.api.bo.PayRefundPageBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundPageBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.dto.PayRefundPageDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundPageDTO;
import cn.iocoder.mall.pay.application.convert.PayRefundConvert; import cn.iocoder.mall.pay.application.convert.PayRefundConvert;
import cn.iocoder.mall.pay.application.vo.admins.AdminsPayRefundPageVO; import cn.iocoder.mall.pay.application.vo.admins.AdminsPayRefundPageVO;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;

View File

@ -2,8 +2,8 @@ package cn.iocoder.mall.pay.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.PayTransactionService;
import cn.iocoder.mall.pay.api.bo.PayTransactionPageBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionPageBO;
import cn.iocoder.mall.pay.api.dto.PayTransactionPageDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionPageDTO;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;

View File

@ -3,11 +3,14 @@ package cn.iocoder.mall.pay.application.controller.users;
import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.PayTransactionService;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionSubmitBO;
import cn.iocoder.mall.pay.api.constant.PayChannelEnum; import cn.iocoder.mall.pay.api.constant.PayChannelEnum;
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionGetDTO;
import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionSubmitDTO;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -18,8 +21,11 @@ import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController @RestController
@RequestMapping("users/transaction") // TODO 芋艿,理论来说,是用户无关的。这里先酱紫先~ @RequestMapping("users/transaction")
@Api("【用户】支付交易 API")
public class UsersPayTransactionController { public class UsersPayTransactionController {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
@ -28,23 +34,19 @@ public class UsersPayTransactionController {
private PayTransactionService payTransactionService; private PayTransactionService payTransactionService;
@GetMapping("/get") @GetMapping("/get")
// TODO result 后面改下 @ApiOperation("获得支付交易")
public CommonResult<PayTransactionBO> get(@RequestParam("appId") String appId, public CommonResult<PayTransactionBO> get(PayTransactionGetDTO payTransactionGetDTO) {
@RequestParam("orderId") String orderId) { payTransactionGetDTO.setUserId(UserSecurityContextHolder.getContext().getUserId());
return payTransactionService.getTransaction(UserSecurityContextHolder.getContext().getUserId(), appId, orderId); return success(payTransactionService.getTransaction(payTransactionGetDTO));
} }
@PostMapping("/submit") // TODO api 注释 @PostMapping("/submit")
// TODO result 后面改下 @ApiOperation("提交支付交易")
public CommonResult<PayTransactionSubmitBO> submit(HttpServletRequest request, public CommonResult<PayTransactionSubmitBO> submit(HttpServletRequest request,
@RequestParam("appId") String appId, PayTransactionSubmitDTO payTransactionSubmitDTO) {
@RequestParam("orderId") String orderId, payTransactionSubmitDTO.setCreateIp(HttpUtil.getIp(request));
@RequestParam("payChannel") Integer payChannel) {
PayTransactionSubmitDTO payTransactionSubmitDTO = new PayTransactionSubmitDTO()
.setAppId(appId).setOrderId(orderId).setPayChannel(payChannel)
.setCreateIp(HttpUtil.getIp(request));
// 提交支付提交 // 提交支付提交
return payTransactionService.submitTransaction(payTransactionSubmitDTO); return success(payTransactionService.submitTransaction(payTransactionSubmitDTO));
} }
@PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE) @PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE)
@ -63,11 +65,7 @@ public class UsersPayTransactionController {
// JSONObject bodyObj = JSON.parseObject(sb.toString()); // JSONObject bodyObj = JSON.parseObject(sb.toString());
// bodyObj.put("webhookId", bodyObj.remove("id")); // bodyObj.put("webhookId", bodyObj.remove("id"));
// String body = bodyObj.toString(); // String body = bodyObj.toString();
CommonResult<Boolean> result = payTransactionService.updateTransactionPaySuccess(PayChannelEnum.PINGXX.getId(), sb.toString()); payTransactionService.updateTransactionPaySuccess(PayChannelEnum.PINGXX.getId(), sb.toString());
if (result.isError()) {
logger.error("[pingxxPaySuccess][message({}) result({})]", sb, result);
return "failure";
}
return "success"; return "success";
} }

View File

@ -1,6 +1,6 @@
package cn.iocoder.mall.pay.application.convert; package cn.iocoder.mall.pay.application.convert;
import cn.iocoder.mall.pay.api.bo.PayRefundBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundBO;
import cn.iocoder.mall.pay.application.vo.admins.AdminsPayRefundDetailVO; import cn.iocoder.mall.pay.application.vo.admins.AdminsPayRefundDetailVO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;

View File

@ -1,7 +1,7 @@
package cn.iocoder.mall.pay.application.vo.admins; package cn.iocoder.mall.pay.application.vo.admins;
import cn.iocoder.mall.pay.api.bo.PayRefundBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,10 +1,10 @@
package cn.iocoder.mall.pay.api; package cn.iocoder.mall.pay.api;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.bo.PayRefundPageBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundPageBO;
import cn.iocoder.mall.pay.api.bo.PayRefundSubmitBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundSubmitBO;
import cn.iocoder.mall.pay.api.dto.PayRefundPageDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundPageDTO;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundSubmitDTO;
public interface PayRefundService { public interface PayRefundService {

View File

@ -1,23 +1,24 @@
package cn.iocoder.mall.pay.api; package cn.iocoder.mall.pay.api;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionPageBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionPageBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionSubmitBO;
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionCreateDTO;
import cn.iocoder.mall.pay.api.dto.PayTransactionPageDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionGetDTO;
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionPageDTO;
import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionSubmitDTO;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
public interface PayTransactionService { public interface PayTransactionService {
CommonResult<PayTransactionBO> getTransaction(Integer userId, String appId, String orderId); PayTransactionBO getTransaction(PayTransactionGetDTO payTransactionGetDTO);
CommonResult<PayTransactionBO> createTransaction(PayTransactionCreateDTO payTransactionCreateDTO); PayTransactionBO createTransaction(PayTransactionCreateDTO payTransactionCreateDTO);
CommonResult<PayTransactionSubmitBO> submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO); PayTransactionSubmitBO submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO);
/** /**
* *
@ -29,7 +30,7 @@ public interface PayTransactionService {
* 使 String 使 AbstractPaySDK * 使 String 使 AbstractPaySDK
* @return * @return
*/ */
CommonResult<Boolean> updateTransactionPaySuccess(Integer payChannel, String params); Boolean updateTransactionPaySuccess(Integer payChannel, String params);
List<PayTransactionBO> getTransactionList(Collection<Integer> ids); List<PayTransactionBO> getTransactionList(Collection<Integer> ids);

View File

@ -1,24 +0,0 @@
package cn.iocoder.mall.pay.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* BO
*/
@Data
@Accessors(chain = true)
public class PayTransactionSubmitBO implements Serializable {
/**
*
*/
private Integer id;
/**
*
*/
private String invokeResponse;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.bo; package cn.iocoder.mall.pay.api.bo.refund;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.bo; package cn.iocoder.mall.pay.api.bo.refund;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.bo; package cn.iocoder.mall.pay.api.bo.refund;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,63 +1,48 @@
package cn.iocoder.mall.pay.api.bo; package cn.iocoder.mall.pay.api.bo.transaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** @ApiModel("支付交易 BO")
* BO
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class PayTransactionBO implements Serializable { public class PayTransactionBO implements Serializable {
/** @ApiModelProperty(value = "交易编号", required = true, example = "POd4RC6a")
*
*/
private Integer id; private Integer id;
/**
* @ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
*/
private String appId; private String appId;
/**
* IP @ApiModelProperty(value = "发起交易的 IP", required = true, example = "192.168.10.1")
*/
private String createIp; private String createIp;
/**
* 线 @ApiModelProperty(value = "订单号不能为空", required = true, example = "1024")
*
* 1. 使 String 线使 String
* 2. appId orderId
*/
private String orderId; private String orderId;
/**
* @ApiModelProperty(value = "商品名", required = true, example = "芋道源码")
*/
private String orderSubject; private String orderSubject;
/**
* @ApiModelProperty(value = "订单商品描述", required = true, example = "绵啾啾的")
*/
private String orderDescription; private String orderDescription;
/**
* @ApiModelProperty(value = "订单商品备注", example = "绵啾啾的")
*/
private String orderMemo; private String orderMemo;
/**
* @ApiModelProperty(value = "支付金额,单位:分。", required = true, example = "10")
*/
private Integer price; private Integer price;
/**
* @ApiModelProperty(value = "订单状态", required = true, example = "1", notes = "参见 PayTransactionStatusEnum 枚举")
*
* @see cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum
*/
private Integer status; private Integer status;
/**
* @ApiModelProperty(value = "交易过期时间", required = true)
*/
private Date expireTime; private Date expireTime;
/** /**
* 线 * 线
*/ */

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.bo; package cn.iocoder.mall.pay.api.bo.transaction;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -0,0 +1,21 @@
package cn.iocoder.mall.pay.api.bo.transaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@ApiModel("支付交易提交结果 BO")
@Data
@Accessors(chain = true)
public class PayTransactionSubmitBO implements Serializable {
@ApiModelProperty(value = "支付交易拓展单编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "调用三方平台的响应结果", required = true)
private String invokeResponse;
}

View File

@ -1,9 +1,13 @@
package cn.iocoder.mall.pay.api.constant; package cn.iocoder.mall.pay.api.constant;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/** /**
* *
*/ */
public enum PayChannelEnum { public enum PayChannelEnum implements IntArrayValuable {
WEIXIN_APP(100, "wx", "微信 App 支付"), WEIXIN_APP(100, "wx", "微信 App 支付"),
WEIXIN_PUB(101, "wxjs", "微信 JS API 支付"), WEIXIN_PUB(101, "wxjs", "微信 JS API 支付"),
@ -13,6 +17,8 @@ public enum PayChannelEnum {
PINGXX(9999, "ping++", "ping++ 支付"), PINGXX(9999, "ping++", "ping++ 支付"),
; ;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayChannelEnum::getId).toArray();
/** /**
* *
*/ */
@ -44,4 +50,9 @@ public enum PayChannelEnum {
return name; return name;
} }
@Override
public int[] array() {
return ARRAYS;
}
} }

View File

@ -1,37 +0,0 @@
package cn.iocoder.mall.pay.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* DTO
*/
@Data
@Accessors(chain = true)
public class PayTransactionSubmitDTO {
/**
*
*/
@NotEmpty(message = "应用编号不能为空")
private String appId;
/**
* IP
*/
@NotEmpty(message = "IP 不能为空")
private String createIp;
/**
* 线
*/
@NotEmpty(message = "订单号不能为空")
private String orderId;
/**
*
*/
@NotNull(message = "支付渠道")
private Integer payChannel;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.dto; package cn.iocoder.mall.pay.api.dto.refund;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.dto; package cn.iocoder.mall.pay.api.dto.refund;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -1,5 +1,7 @@
package cn.iocoder.mall.pay.api.dto; package cn.iocoder.mall.pay.api.dto.transaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
@ -10,54 +12,43 @@ import javax.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** @ApiModel("支付交易创建 DTO")
* DTO
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class PayTransactionCreateDTO implements Serializable { public class PayTransactionCreateDTO implements Serializable {
/** @ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
*
*/
@NotEmpty(message = "应用编号不能为空") @NotEmpty(message = "应用编号不能为空")
private String appId; private String appId;
/**
* IP @ApiModelProperty(value = "发起交易的 IP", required = true, example = "192.168.10.1")
*/
@NotEmpty(message = "IP 不能为空") @NotEmpty(message = "IP 不能为空")
private String createIp; private String createIp;
/**
* 线 @ApiModelProperty(value = "订单号不能为空", required = true, example = "1024")
*/
@NotEmpty(message = "订单号不能为空") @NotEmpty(message = "订单号不能为空")
private String orderId; private String orderId;
/**
* @ApiModelProperty(value = "商品名", required = true, example = "芋道源码")
*/
@NotEmpty(message = "商品名不能为空") @NotEmpty(message = "商品名不能为空")
@Length(max = 32, message = "商品名不能超过32") @Length(max = 32, message = "商品名不能超过32")
private String orderSubject; private String orderSubject;
/**
* @ApiModelProperty(value = "订单商品描述", required = true, example = "绵啾啾的")
*/
@NotEmpty(message = "商品描述不能为空") @NotEmpty(message = "商品描述不能为空")
@Length(max = 128, message = "商品描述长度不能超过128") @Length(max = 128, message = "商品描述长度不能超过128")
private String orderDescription; private String orderDescription;
/**
* @ApiModelProperty(value = "订单商品备注", example = "绵啾啾的")
*/ @Length(max = 256, message = "商品备注长度不能超过256")
@Length(max = 256, message = "商品描述长度不能超过256")
private String orderMemo; private String orderMemo;
/**
* @ApiModelProperty(value = "支付金额,单位:分。", required = true, example = "10")
*/
@NotNull(message = "金额不能为空") @NotNull(message = "金额不能为空")
@DecimalMin(value = "0", inclusive = false, message = "金额必须大于零") @DecimalMin(value = "0", inclusive = false, message = "金额必须大于零")
private Integer price; private Integer price;
/**
* @ApiModelProperty(value = "交易过期时间", required = true)
*/
@NotNull(message = "交易过期时间不能为空") @NotNull(message = "交易过期时间不能为空")
private Date expireTime; private Date expireTime;

View File

@ -0,0 +1,28 @@
package cn.iocoder.mall.pay.api.dto.transaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("支付交易获得 DTO")
@Data
@Accessors(chain = true)
public class PayTransactionGetDTO {
@ApiModelProperty(value = "用户编号", required = true, example = "1", hidden = true) // hidden 的原因是Service DTO 自己传入,无需暴露的 Controller API 里
@NotNull(message = "用户编号不能为空")
private Integer userId;
@ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
@NotEmpty(message = "应用编号不能为空")
private String appId;
@ApiModelProperty(value = "订单号不能为空", required = true, example = "1024")
@NotEmpty(message = "订单号不能为空")
private String orderId;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.pay.api.dto; package cn.iocoder.mall.pay.api.dto.transaction;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;

View File

@ -0,0 +1,35 @@
package cn.iocoder.mall.pay.api.dto.transaction;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.pay.api.constant.PayChannelEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("支付交易提交 DTO")
@Data
@Accessors(chain = true)
public class PayTransactionSubmitDTO {
@ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
@NotEmpty(message = "应用编号不能为空")
private String appId;
@ApiModelProperty(value = "发起交易的 IP", required = true, example = "192.168.10.1", hidden = true) // hidden 的原因是Service DTO 自己传入,无需暴露的 Controller API 里
@NotEmpty(message = "IP 不能为空")
private String createIp;
@ApiModelProperty(value = "订单号", required = true, example = "1024")
@NotEmpty(message = "订单号不能为空")
private String orderId;
@ApiModelProperty(value = "支付渠道", required = true, example = "1", notes = "参见 PayChannelEnum 枚举")
@InEnum(value = PayChannelEnum.class, message = "支付渠道必须是 {value}")
@NotNull(message = "支付渠道")
private Integer payChannel;
}

View File

@ -1,17 +1,20 @@
package cn.iocoder.mall.pay.biz.component; package cn.iocoder.mall.pay.biz.component;
import org.apache.dubbo.config.ApplicationConfig; import cn.iocoder.common.framework.util.StringUtil;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import lombok.Data; import lombok.Data;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.List;
@Component @Component
public class DubboReferencePool { public class DubboReferencePool {
@ -44,7 +47,8 @@ public class DubboReferencePool {
private String dubboApplicationName; private String dubboApplicationName;
private ReferenceMeta createGenericService(String notifyUrl) { private ReferenceMeta createGenericService(String notifyUrl) {
String[] notifyUrlParts = notifyUrl.split("#"); // 使用 # 号分隔,格式为 服务名#方法名#版本号
List<String> notifyUrlParts = StringUtil.split(notifyUrl, "#");
// 创建 ApplicationConfig 对象 // 创建 ApplicationConfig 对象
ApplicationConfig application = new ApplicationConfig(); ApplicationConfig application = new ApplicationConfig();
application.setName(dubboApplicationName); application.setName(dubboApplicationName);
@ -55,14 +59,14 @@ public class DubboReferencePool {
application.setRegistry(registry); application.setRegistry(registry);
// 创建 ReferenceConfig 对象 // 创建 ReferenceConfig 对象
ReferenceConfig<GenericService> reference = new ReferenceConfig<>(); ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
reference.setInterface(notifyUrlParts[0]); // 弱类型接口名 reference.setInterface(notifyUrlParts.get(0)); // 弱类型接口名
reference.setGeneric(true); // 声明为泛化接口 reference.setGeneric(true); // 声明为泛化接口
reference.setApplication(application); reference.setApplication(application);
// reference.setVersion("*"); // TODO 芋艿,后面要优化下。 reference.setVersion(notifyUrlParts.size() > 2 ? notifyUrlParts.get(2) : "1.0.0"); // 如果未配置服务的版本号,则默认使用 1.0.0
// 获得 GenericService 对象 // 获得 GenericService 对象
GenericService genericService = reference.get(); GenericService genericService = reference.get();
// 构建最终的 ReferenceMeta 对象 // 构建最终的 ReferenceMeta 对象
return new ReferenceMeta(reference, genericService, notifyUrlParts[1]); return new ReferenceMeta(reference, genericService, notifyUrlParts.get(1));
} }
public ReferenceMeta getReferenceMeta(String notifyUrl) { public ReferenceMeta getReferenceMeta(String notifyUrl) {

View File

@ -1,7 +1,7 @@
package cn.iocoder.mall.pay.biz.convert; package cn.iocoder.mall.pay.biz.convert;
import cn.iocoder.mall.pay.api.bo.PayRefundBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundBO;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundSubmitDTO;
import cn.iocoder.mall.pay.biz.dataobject.PayRefundDO; import cn.iocoder.mall.pay.biz.dataobject.PayRefundDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;

View File

@ -1,8 +1,8 @@
package cn.iocoder.mall.pay.biz.convert; package cn.iocoder.mall.pay.biz.convert;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionCreateDTO;
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionSubmitDTO;
import cn.iocoder.mall.pay.biz.dataobject.PayTransactionDO; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionDO;
import cn.iocoder.mall.pay.biz.dataobject.PayTransactionExtensionDO; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionExtensionDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;

View File

@ -18,6 +18,7 @@ import java.util.Date;
topic = PayRefundSuccessMessage.TOPIC, topic = PayRefundSuccessMessage.TOPIC,
consumerGroup = "pay-consumer-group-" + PayRefundSuccessMessage.TOPIC consumerGroup = "pay-consumer-group-" + PayRefundSuccessMessage.TOPIC
) )
@Deprecated // 艿艿:突然发现,业务方实际无需回调。参考了 https://help.youzan.com/displaylist/detail_4_998 的文章。业务方,只要记录下退款单号,进行关联即可。
public class PayRefundSuccessConsumer extends AbstractPayNotifySuccessConsumer<PayRefundSuccessMessage> public class PayRefundSuccessConsumer extends AbstractPayNotifySuccessConsumer<PayRefundSuccessMessage>
implements RocketMQListener<PayRefundSuccessMessage> { implements RocketMQListener<PayRefundSuccessMessage> {

View File

@ -2,7 +2,6 @@ package cn.iocoder.mall.pay.biz.service;
import cn.iocoder.common.framework.constant.CommonStatusEnum; import cn.iocoder.common.framework.constant.CommonStatusEnum;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum; import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum;
import cn.iocoder.mall.pay.biz.dao.PayAppMapper; import cn.iocoder.mall.pay.biz.dao.PayAppMapper;
import cn.iocoder.mall.pay.biz.dataobject.PayAppDO; import cn.iocoder.mall.pay.biz.dataobject.PayAppDO;
@ -15,17 +14,17 @@ public class PayAppServiceImpl {
@Autowired @Autowired
private PayAppMapper payAppMapper; private PayAppMapper payAppMapper;
public CommonResult<PayAppDO> validPayApp(String appId) { public PayAppDO validPayApp(String appId) {
PayAppDO payAppDO = payAppMapper.selectById(appId); PayAppDO payAppDO = payAppMapper.selectById(appId);
// 校验是否存在 // 校验是否存在
if (payAppDO == null) { if (payAppDO == null) {
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_APP_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_APP_NOT_FOUND.getCode());
} }
// 校验是否禁用 // 校验是否禁用
if (CommonStatusEnum.DISABLE.getValue().equals(payAppDO.getStatus())) { if (CommonStatusEnum.DISABLE.getValue().equals(payAppDO.getStatus())) {
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_APP_IS_DISABLE.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_APP_IS_DISABLE.getCode());
} }
return CommonResult.success(payAppDO); return payAppDO;
} }
} }

View File

@ -27,6 +27,7 @@ public class PayNotifyServiceImpl {
@Resource @Resource
private RocketMQTemplate rocketMQTemplate; private RocketMQTemplate rocketMQTemplate;
@Deprecated // 参见 PayRefundSuccessConsumer 类的说明
public void addRefundNotifyTask(PayRefundDO refund) { public void addRefundNotifyTask(PayRefundDO refund) {
PayNotifyTaskDO payTransactionNotifyTask = this.createBasePayNotifyTaskDO(refund.getAppId(), refund.getNotifyUrl()) PayNotifyTaskDO payTransactionNotifyTask = this.createBasePayNotifyTaskDO(refund.getAppId(), refund.getNotifyUrl())
.setType(PayNotifyType.REFUND.getValue()); .setType(PayNotifyType.REFUND.getValue());

View File

@ -5,13 +5,13 @@ import cn.iocoder.common.framework.util.MathUtil;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.PayRefundService; import cn.iocoder.mall.pay.api.PayRefundService;
import cn.iocoder.mall.pay.api.bo.PayRefundPageBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundPageBO;
import cn.iocoder.mall.pay.api.bo.PayRefundSubmitBO; import cn.iocoder.mall.pay.api.bo.refund.PayRefundSubmitBO;
import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum; import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum;
import cn.iocoder.mall.pay.api.constant.PayRefundStatus; import cn.iocoder.mall.pay.api.constant.PayRefundStatus;
import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum; import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum;
import cn.iocoder.mall.pay.api.dto.PayRefundPageDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundPageDTO;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundSubmitDTO;
import cn.iocoder.mall.pay.biz.client.AbstractPaySDK; import cn.iocoder.mall.pay.biz.client.AbstractPaySDK;
import cn.iocoder.mall.pay.biz.client.PaySDKFactory; import cn.iocoder.mall.pay.biz.client.PaySDKFactory;
import cn.iocoder.mall.pay.biz.client.RefundSuccessBO; import cn.iocoder.mall.pay.biz.client.RefundSuccessBO;
@ -51,13 +51,9 @@ public class PayRefundServiceImpl implements PayRefundService {
private RocketMQTemplate rocketMQTemplate; private RocketMQTemplate rocketMQTemplate;
@Override @Override
@SuppressWarnings("Duplicates")
public CommonResult<PayRefundSubmitBO> submitRefund(PayRefundSubmitDTO payRefundSubmitDTO) { public CommonResult<PayRefundSubmitBO> submitRefund(PayRefundSubmitDTO payRefundSubmitDTO) {
// 校验 App 是否有效 // 校验 App 是否有效
CommonResult<PayAppDO> appResult = payAppService.validPayApp(payRefundSubmitDTO.getAppId()); PayAppDO payAppDO = payAppService.validPayApp(payRefundSubmitDTO.getAppId());
if (appResult.isError()) {
return CommonResult.error(appResult);
}
// 获得 PayTransactionDO ,并校验其是否存在 // 获得 PayTransactionDO ,并校验其是否存在
PayTransactionDO payTransaction = payTransactionService.getTransaction(payRefundSubmitDTO.getAppId(), payRefundSubmitDTO.getOrderId()); PayTransactionDO payTransaction = payTransactionService.getTransaction(payRefundSubmitDTO.getAppId(), payRefundSubmitDTO.getOrderId());
if (payTransaction == null) { // 是否存在 if (payTransaction == null) { // 是否存在
@ -82,7 +78,7 @@ public class PayRefundServiceImpl implements PayRefundService {
.setTransactionId(payTransaction.getId()) .setTransactionId(payTransaction.getId())
.setRefundCode(generateTransactionCode()) // TODO 芋艿,后续调整 .setRefundCode(generateTransactionCode()) // TODO 芋艿,后续调整
.setStatus(PayRefundStatus.WAITING.getValue()) .setStatus(PayRefundStatus.WAITING.getValue())
.setNotifyUrl(appResult.getData().getRefundNotifyUrl()) .setNotifyUrl(payAppDO.getRefundNotifyUrl())
.setRefundChannel(payTransaction.getPayChannel()); .setRefundChannel(payTransaction.getPayChannel());
payRefundDO.setCreateTime(new Date()); payRefundDO.setCreateTime(new Date());
payRefundMapper.insert(payRefundDO); payRefundMapper.insert(payRefundDO);

View File

@ -5,14 +5,15 @@ import cn.iocoder.common.framework.util.MathUtil;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.PayTransactionService;
import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionPageBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionPageBO;
import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO; import cn.iocoder.mall.pay.api.bo.transaction.PayTransactionSubmitBO;
import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum; import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum;
import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum; import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum;
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionCreateDTO;
import cn.iocoder.mall.pay.api.dto.PayTransactionPageDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionGetDTO;
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionPageDTO;
import cn.iocoder.mall.pay.api.dto.transaction.PayTransactionSubmitDTO;
import cn.iocoder.mall.pay.biz.client.AbstractPaySDK; import cn.iocoder.mall.pay.biz.client.AbstractPaySDK;
import cn.iocoder.mall.pay.biz.client.PaySDKFactory; import cn.iocoder.mall.pay.biz.client.PaySDKFactory;
import cn.iocoder.mall.pay.biz.client.TransactionSuccessBO; import cn.iocoder.mall.pay.biz.client.TransactionSuccessBO;
@ -68,23 +69,21 @@ public class PayTransactionServiceImpl implements PayTransactionService {
} }
@Override @Override
public CommonResult<PayTransactionBO> getTransaction(Integer userId, String appId, String orderId) { public PayTransactionBO getTransaction(PayTransactionGetDTO payTransactionGetDTO) {
PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(appId, orderId); PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(payTransactionGetDTO.getAppId(),
payTransactionGetDTO.getOrderId());
if (payTransaction == null) { if (payTransaction == null) {
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode());
} }
// TODO 芋艿 userId 的校验 // TODO 芋艿 userId 的校验
return CommonResult.success(PayTransactionConvert.INSTANCE.convert(payTransaction)); return PayTransactionConvert.INSTANCE.convert(payTransaction);
} }
@Override @Override
@SuppressWarnings("Duplicates") @SuppressWarnings("Duplicates")
public CommonResult<PayTransactionBO> createTransaction(PayTransactionCreateDTO payTransactionCreateDTO) { public PayTransactionBO createTransaction(PayTransactionCreateDTO payTransactionCreateDTO) {
// 校验 App // 校验 App
CommonResult<PayAppDO> appResult = payAppService.validPayApp(payTransactionCreateDTO.getAppId()); PayAppDO payAppDO = payAppService.validPayApp(payTransactionCreateDTO.getAppId());
if (appResult.isError()) {
return CommonResult.error(appResult);
}
// 插入 PayTransactionDO // 插入 PayTransactionDO
PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId( PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(
payTransactionCreateDTO.getAppId(), payTransactionCreateDTO.getOrderId()); payTransactionCreateDTO.getAppId(), payTransactionCreateDTO.getOrderId());
@ -95,31 +94,28 @@ public class PayTransactionServiceImpl implements PayTransactionService {
} else { } else {
payTransaction = PayTransactionConvert.INSTANCE.convert(payTransactionCreateDTO); payTransaction = PayTransactionConvert.INSTANCE.convert(payTransactionCreateDTO);
payTransaction.setStatus(PayTransactionStatusEnum.WAITING.getValue()) payTransaction.setStatus(PayTransactionStatusEnum.WAITING.getValue())
.setNotifyUrl(appResult.getData().getNotifyUrl()); .setNotifyUrl(payAppDO.getNotifyUrl());
payTransaction.setCreateTime(new Date()); payTransaction.setCreateTime(new Date());
payTransactionMapper.insert(payTransaction); payTransactionMapper.insert(payTransaction);
} }
// 返回成功 // 返回成功
return CommonResult.success(PayTransactionConvert.INSTANCE.convert(payTransaction)); return PayTransactionConvert.INSTANCE.convert(payTransaction);
} }
@Override @Override
@SuppressWarnings("Duplicates") @SuppressWarnings("Duplicates")
public CommonResult<PayTransactionSubmitBO> submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO) { public PayTransactionSubmitBO submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO) {
// TODO 校验支付渠道是否有效 // TODO 校验支付渠道是否有效
// 校验 App 是否有效 // 校验 App 是否有效
CommonResult<PayAppDO> appResult = payAppService.validPayApp(payTransactionSubmitDTO.getAppId()); payAppService.validPayApp(payTransactionSubmitDTO.getAppId());
if (appResult.isError()) {
return CommonResult.error(appResult);
}
// 获得 PayTransactionDO ,并校验其是否存在 // 获得 PayTransactionDO ,并校验其是否存在
PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId( PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(
payTransactionSubmitDTO.getAppId(), payTransactionSubmitDTO.getOrderId()); payTransactionSubmitDTO.getAppId(), payTransactionSubmitDTO.getOrderId());
if (payTransaction == null) { // 是否存在 if (payTransaction == null) { // 是否存在
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode());
} }
if (!PayTransactionStatusEnum.WAITING.getValue().equals(payTransaction.getStatus())) { // 校验状态,必须是待支付 if (!PayTransactionStatusEnum.WAITING.getValue().equals(payTransaction.getStatus())) { // 校验状态,必须是待支付
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode());
} }
// 插入 PayTransactionExtensionDO // 插入 PayTransactionExtensionDO
PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(payTransactionSubmitDTO) PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(payTransactionSubmitDTO)
@ -131,33 +127,32 @@ public class PayTransactionServiceImpl implements PayTransactionService {
AbstractPaySDK paySDK = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel()); AbstractPaySDK paySDK = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel());
CommonResult<String> invokeResult = paySDK.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null CommonResult<String> invokeResult = paySDK.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null
if (invokeResult.isError()) { if (invokeResult.isError()) {
return CommonResult.error(invokeResult); throw ServiceExceptionUtil.exception(invokeResult.getCode(), invokeResult.getMessage());
} }
// TODO 轮询三方接口,是否已经支付的任务 // TODO 轮询三方接口,是否已经支付的任务
// 返回成功 // 返回成功
PayTransactionSubmitBO payTransactionSubmitBO = new PayTransactionSubmitBO() return new PayTransactionSubmitBO().setId(payTransactionExtensionDO.getId())
.setId(payTransactionExtensionDO.getId()).setInvokeResponse(invokeResult.getData()); .setInvokeResponse(invokeResult.getData());
return CommonResult.success(payTransactionSubmitBO);
} }
@Override @Override
@Transactional @Transactional
public CommonResult<Boolean> updateTransactionPaySuccess(Integer payChannel, String params) { public Boolean updateTransactionPaySuccess(Integer payChannel, String params) {
// TODO 芋艿,记录回调日志 // TODO 芋艿,记录回调日志
// 解析传入的参数,成 TransactionSuccessBO 对象 // 解析传入的参数,成 TransactionSuccessBO 对象
AbstractPaySDK paySDK = PaySDKFactory.getSDK(payChannel); AbstractPaySDK paySDK = PaySDKFactory.getSDK(payChannel);
CommonResult<TransactionSuccessBO> paySuccessResult = paySDK.parseTransactionSuccessParams(params); CommonResult<TransactionSuccessBO> paySuccessResult = paySDK.parseTransactionSuccessParams(params);
if (paySuccessResult.isError()) { if (paySuccessResult.isError()) {
return CommonResult.error(paySuccessResult); throw ServiceExceptionUtil.exception(paySuccessResult.getCode(), paySuccessResult.getMessage());
} }
// TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。 // TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。
// 1.1 查询 PayTransactionExtensionDO // 1.1 查询 PayTransactionExtensionDO
PayTransactionExtensionDO extension = payTransactionExtensionMapper.selectByTransactionCode(paySuccessResult.getData().getTransactionCode()); PayTransactionExtensionDO extension = payTransactionExtensionMapper.selectByTransactionCode(paySuccessResult.getData().getTransactionCode());
if (extension == null) { if (extension == null) {
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_NOT_FOUND.getCode());
} }
if (!PayTransactionStatusEnum.WAITING.getValue().equals(extension.getStatus())) { // 校验状态,必须是待支付 if (!PayTransactionStatusEnum.WAITING.getValue().equals(extension.getStatus())) { // 校验状态,必须是待支付
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode());
} }
// 1.2 更新 PayTransactionExtensionDO // 1.2 更新 PayTransactionExtensionDO
PayTransactionExtensionDO updatePayTransactionExtension = new PayTransactionExtensionDO() PayTransactionExtensionDO updatePayTransactionExtension = new PayTransactionExtensionDO()
@ -172,7 +167,7 @@ public class PayTransactionServiceImpl implements PayTransactionService {
// 2.1 判断 PayTransactionDO 是否处于待支付 // 2.1 判断 PayTransactionDO 是否处于待支付
PayTransactionDO transaction = payTransactionMapper.selectById(extension.getTransactionId()); PayTransactionDO transaction = payTransactionMapper.selectById(extension.getTransactionId());
if (transaction == null) { if (transaction == null) {
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode());
} }
if (!PayTransactionStatusEnum.WAITING.getValue().equals(transaction.getStatus())) { // 校验状态,必须是待支付 if (!PayTransactionStatusEnum.WAITING.getValue().equals(transaction.getStatus())) { // 校验状态,必须是待支付
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode());
@ -191,10 +186,10 @@ public class PayTransactionServiceImpl implements PayTransactionService {
throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode());
} }
logger.info("[updateTransactionPaySuccess][PayTransactionDO({}) 更新为已支付]", transaction.getId()); logger.info("[updateTransactionPaySuccess][PayTransactionDO({}) 更新为已支付]", transaction.getId());
// 3 新增 PayNotifyTaskDO // 3 新增 PayNotifyTaskDO 注释原因,参见 PayRefundSuccessConsumer 类。
payNotifyService.addTransactionNotifyTask(transaction, extension); // payNotifyService.addTransactionNotifyTask(transaction, extension);
// 返回结果 // 返回结果
return CommonResult.success(true); return true;
} }
@Override @Override

View File

@ -1,7 +1,7 @@
package cn.iocoder.mall.pay.biz.service; package cn.iocoder.mall.pay.biz.service;
import cn.iocoder.mall.pay.api.PayRefundService; import cn.iocoder.mall.pay.api.PayRefundService;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO; import cn.iocoder.mall.pay.api.dto.refund.PayRefundSubmitDTO;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -1,10 +1,6 @@
package cn.iocoder.mall.pay.biz.service; package cn.iocoder.mall.pay.biz.service;
import cn.iocoder.mall.pay.api.dto.PayRefundSubmitDTO;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

View File

@ -5,11 +5,10 @@ import cn.iocoder.mall.product.api.ProductCategoryService;
import cn.iocoder.mall.product.api.bo.ProductCategoryBO; import cn.iocoder.mall.product.api.bo.ProductCategoryBO;
import cn.iocoder.mall.product.application.convert.ProductCategoryConvert; import cn.iocoder.mall.product.application.convert.ProductCategoryConvert;
import cn.iocoder.mall.product.application.vo.users.UsersProductCategoryVO; import cn.iocoder.mall.product.application.vo.users.UsersProductCategoryVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import org.apache.dubbo.config.annotation.Reference;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -30,7 +29,6 @@ public class UsersProductCategoryController {
@GetMapping("/list") @GetMapping("/list")
@ApiOperation("获得指定编号下的子分类的数组") @ApiOperation("获得指定编号下的子分类的数组")
@ApiImplicitParam(name = "pid", value = "指定分类编号", required = true, example = "0") @ApiImplicitParam(name = "pid", value = "指定分类编号", required = true, example = "0")
@PermitAll
public CommonResult<List<UsersProductCategoryVO>> list(@RequestParam("pid") Integer pid) { public CommonResult<List<UsersProductCategoryVO>> list(@RequestParam("pid") Integer pid) {
List<ProductCategoryBO> result = productCategoryService.getListByPid(pid); List<ProductCategoryBO> result = productCategoryService.getListByPid(pid);
return CommonResult.success(ProductCategoryConvert.Users.INSTANCE.convertToVO(result)); return CommonResult.success(ProductCategoryConvert.Users.INSTANCE.convertToVO(result));

View File

@ -7,7 +7,6 @@ import cn.iocoder.mall.product.api.dto.ProductSpuPageDTO;
import cn.iocoder.mall.product.application.convert.ProductSpuConvert; import cn.iocoder.mall.product.application.convert.ProductSpuConvert;
import cn.iocoder.mall.product.application.vo.users.UsersProductSpuDetailVO; import cn.iocoder.mall.product.application.vo.users.UsersProductSpuDetailVO;
import cn.iocoder.mall.product.application.vo.users.UsersProductSpuPageVO; import cn.iocoder.mall.product.application.vo.users.UsersProductSpuPageVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
@ -31,7 +30,6 @@ public class UsersProductSpuController {
@GetMapping("/info") @GetMapping("/info")
@ApiOperation("商品 SPU 明细") @ApiOperation("商品 SPU 明细")
@ApiImplicitParam(name = "id", value = "SPU 编号", required = true, example = "100") @ApiImplicitParam(name = "id", value = "SPU 编号", required = true, example = "100")
@PermitAll
public CommonResult<UsersProductSpuDetailVO> info(@RequestParam("id") Integer id) { public CommonResult<UsersProductSpuDetailVO> info(@RequestParam("id") Integer id) {
return success(ProductSpuConvert.INSTANCE.convert4(productSpuService.getProductSpuDetail(id))); return success(ProductSpuConvert.INSTANCE.convert4(productSpuService.getProductSpuDetail(id)));
} }
@ -43,7 +41,6 @@ public class UsersProductSpuController {
@ApiImplicitParam(name = "pageNo", value = "页码,从 1 开始", example = "1"), @ApiImplicitParam(name = "pageNo", value = "页码,从 1 开始", example = "1"),
@ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"), @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"),
}) })
@PermitAll
@Deprecated // 使用商品搜索接口 @Deprecated // 使用商品搜索接口
public CommonResult<UsersProductSpuPageVO> page(@RequestParam(value = "cid", required = false) Integer cid, public CommonResult<UsersProductSpuPageVO> page(@RequestParam(value = "cid", required = false) Integer cid,
@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo, @RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,

View File

@ -6,7 +6,6 @@ import cn.iocoder.mall.promotion.api.BannerService;
import cn.iocoder.mall.promotion.api.bo.BannerBO; import cn.iocoder.mall.promotion.api.bo.BannerBO;
import cn.iocoder.mall.promotion.application.convert.BannerConvert; import cn.iocoder.mall.promotion.application.convert.BannerConvert;
import cn.iocoder.mall.promotion.application.vo.users.UsersBannerVO; import cn.iocoder.mall.promotion.application.vo.users.UsersBannerVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
@ -27,7 +26,6 @@ public class UsersBannerController {
@GetMapping("/list") @GetMapping("/list")
@ApiOperation("获得所有 Banner 列表") @ApiOperation("获得所有 Banner 列表")
@PermitAll
public CommonResult<List<UsersBannerVO>> list() { public CommonResult<List<UsersBannerVO>> list() {
// 查询 Banner 列表 // 查询 Banner 列表
List<BannerBO> result = bannerService.getBannerListByStatus(CommonStatusEnum.ENABLE.getValue()); List<BannerBO> result = bannerService.getBannerListByStatus(CommonStatusEnum.ENABLE.getValue());

View File

@ -11,7 +11,6 @@ import cn.iocoder.mall.promotion.application.convert.CouponTemplateConvert;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardPageVO; import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardPageVO;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardVO; import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardVO;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponTemplateVO; import cn.iocoder.mall.promotion.application.vo.users.UsersCouponTemplateVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
@ -35,7 +34,6 @@ public class UsersCouponController {
@GetMapping("/template/get") @GetMapping("/template/get")
@ApiOperation(value = "优惠劵(码)模板信息") @ApiOperation(value = "优惠劵(码)模板信息")
@ApiImplicitParam(name = "id", value = "优惠劵(码)模板编号", required = true, example = "10") @ApiImplicitParam(name = "id", value = "优惠劵(码)模板编号", required = true, example = "10")
@PermitAll
public CommonResult<UsersCouponTemplateVO> templateGet(@RequestParam("id") Integer id) { public CommonResult<UsersCouponTemplateVO> templateGet(@RequestParam("id") Integer id) {
CouponTemplateBO template = couponService.getCouponTemplate(id); CouponTemplateBO template = couponService.getCouponTemplate(id);
return success(CouponTemplateConvert.USERS.convert2(template)); return success(CouponTemplateConvert.USERS.convert2(template));

View File

@ -8,7 +8,6 @@ import cn.iocoder.mall.promotion.api.ProductRecommendService;
import cn.iocoder.mall.promotion.api.bo.ProductRecommendBO; import cn.iocoder.mall.promotion.api.bo.ProductRecommendBO;
import cn.iocoder.mall.promotion.application.convert.ProductRecommendConvert; import cn.iocoder.mall.promotion.application.convert.ProductRecommendConvert;
import cn.iocoder.mall.promotion.application.vo.users.UsersProductRecommendVO; import cn.iocoder.mall.promotion.application.vo.users.UsersProductRecommendVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -36,7 +35,6 @@ public class UsersProductRecommendController {
@GetMapping("/list") @GetMapping("/list")
@ApiOperation("获得所有 Banner 列表") @ApiOperation("获得所有 Banner 列表")
@PermitAll
public CommonResult<Map<Integer, Collection<UsersProductRecommendVO>>> list() { public CommonResult<Map<Integer, Collection<UsersProductRecommendVO>>> list() {
// 查询商品推荐列表 // 查询商品推荐列表
List<ProductRecommendBO> productRecommends = productRecommendService.getProductRecommendList( List<ProductRecommendBO> productRecommends = productRecommendService.getProductRecommendList(

View File

@ -12,11 +12,6 @@ spring:
max-active: 5 max-active: 5
max-wait: 10000 max-wait: 10000
# mybatis
#mybatis:
# config-location: classpath:mybatis-config.xml
# mapper-locations: classpath:mapper/*.xml
# type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject
# mybatis-plus # mybatis-plus
mybatis-plus: mybatis-plus:
configuration: configuration:
@ -26,7 +21,7 @@ mybatis-plus:
id-type: auto id-type: auto
mapper-locations: classpath*:mapper/*.xml mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject
config-location: classpath:mybatis-config.xml
# dubbo # dubbo
dubbo: dubbo:
application: application:

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 使用驼峰命名法转换字段。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
</typeAliases>
</configuration>

BIN
sessionStore/root.data Normal file

Binary file not shown.

View File

@ -12,7 +12,17 @@ import java.util.Set;
@Accessors(chain = true) @Accessors(chain = true)
public class AdminSecurityContext { public class AdminSecurityContext {
/**
*
*/
private Integer adminId; private Integer adminId;
/**
*
*/
private String username;
/**
*
*/
private Set<Integer> roleIds; private Set<Integer> roleIds;
} }

View File

@ -0,0 +1,31 @@
package cn.iocoder.mall.admin.sdk.interceptor;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.mall.admin.api.constant.AdminConstants;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Admin
*
* 使 POST
*/
@Component
public class AdminDemoInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (AdminConstants.USERNAME_DEMO.equals(AdminSecurityContextHolder.getContext().getUsername())
&& request.getMethod().equalsIgnoreCase(HttpMethod.POST.toString())) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_DEMO_CAN_NOT_WRITE.getCode());
}
return true;
}
}

View File

@ -89,6 +89,7 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
context.setAdminId(authentication.getUserId()); context.setAdminId(authentication.getUserId());
MallUtil.setUserId(request, authentication.getUserId()); // 记录到 request 中,避免 AdminSecurityContext 后续清理掉后,其它地方需要用到 userId MallUtil.setUserId(request, authentication.getUserId()); // 记录到 request 中,避免 AdminSecurityContext 后续清理掉后,其它地方需要用到 userId
if (authorization != null) { if (authorization != null) {
context.setUsername(authorization.getUsername());
context.setRoleIds(authorization.getRoleIds()); context.setRoleIds(authorization.getRoleIds());
} }
} }
@ -113,8 +114,4 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
requiresPermissions != null ? Arrays.asList(requiresPermissions.value()) : null); requiresPermissions != null ? Arrays.asList(requiresPermissions.value()) : null);
} }
private void checkPermission() {
}
} }

View File

@ -1,6 +1,6 @@
/** /**
* SDK 使 * SDK 使
* *
* 1. {@link cn.iocoder.mall.admin.sdk.interceptor.UserSecurityInterceptor} URL * 1. {@link cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor} URL
*/ */
package cn.iocoder.mall.admin.sdk; package cn.iocoder.mall.admin.sdk;

View File

@ -17,7 +17,7 @@ import java.util.Map;
public interface AdminService { public interface AdminService {
/** /**
* *
* *
* + * +
* *

View File

@ -4,6 +4,8 @@ import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RefreshTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RemoveTokenByUserDTO;
/** /**
* Oauth2 * Oauth2
@ -18,7 +20,20 @@ public interface OAuth2Service {
*/ */
OAuth2AccessTokenBO createToken(OAuth2CreateTokenDTO oauth2CreateTokenDTO); OAuth2AccessTokenBO createToken(OAuth2CreateTokenDTO oauth2CreateTokenDTO);
// TODO @see 刷新 token /**
* accessToken
*
* @param oauth2RemoveTokenDTO accessToken
*/
void removeToken(OAuth2RemoveTokenByUserDTO oauth2RemoveTokenDTO);
/**
* accessToken
*
* @param oauth2RefreshTokenDTO refreshToken
* @return accessToken
*/
OAuth2AccessTokenBO refreshToken(OAuth2RefreshTokenDTO oauth2RefreshTokenDTO);
/** /**
* accessToken * accessToken

View File

@ -5,16 +5,20 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Set; import java.util.Set;
@ApiModel("管理员授权 BO") @ApiModel("管理员授权 BO")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class AdminAuthorizationBO { public class AdminAuthorizationBO implements Serializable {
@ApiModelProperty(value = "管理员编号", required = true, example = "1") @ApiModelProperty(value = "管理员编号", required = true, example = "1")
private Integer id; private Integer id;
@ApiModelProperty(value = "登陆账号", required = true, example = "1")
private String username;
@ApiModelProperty(value = "角色编号数组", required = true, example = "1") @ApiModelProperty(value = "角色编号数组", required = true, example = "1")
private Set<Integer> roleIds; private Set<Integer> roleIds;

View File

@ -5,10 +5,12 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
@ApiModel("OAUTH2 认证 BO") @ApiModel("OAUTH2 认证 BO")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class OAuth2AuthenticationBO { public class OAuth2AuthenticationBO implements Serializable {
@ApiModelProperty(value = "用户编号", required = true, example = "1") @ApiModelProperty(value = "用户编号", required = true, example = "1")
private Integer userId; private Integer userId;

View File

@ -1,27 +0,0 @@
package cn.iocoder.mall.admin.api.bo.oauth2;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Set;
/**
* OAUTH2 BO
*/
@Data
@Accessors(chain = true)
public class OAuth2AuthenticationOldBO implements Serializable {
/**
*
*/
private Integer adminId;
/**
*
*/
private Set<Integer> roleIds;
}

View File

@ -2,6 +2,14 @@ package cn.iocoder.mall.admin.api.constant;
public class AdminConstants { public class AdminConstants {
/**
* -
*/
public static final String USERNAME_ADMIN = "admin"; public static final String USERNAME_ADMIN = "admin";
/**
* -
*/
public static final String USERNAME_DEMO = "yudaoyuanma";
} }

View File

@ -17,8 +17,9 @@ public enum AdminErrorCodeEnum {
OAUTH2_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"), OAUTH2_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"),
OAUTH2_NOT_LOGIN(1002001015, "账号未登陆"), OAUTH2_NOT_LOGIN(1002001015, "账号未登陆"),
OAUTH2_INVALID_TOKEN_ERROR_USER_TYPE(1002001016, "访问令牌用户类型不正确"), OAUTH2_INVALID_TOKEN_ERROR_USER_TYPE(1002001016, "访问令牌用户类型不正确"),
OAUTH_INVALID_REFRESH_TOKEN_NOT_FOUND(1002001017, "刷新令牌不存在"),
OAUTH_INVALID_TOKEN(1002001020, ""), // 预留 OAUTH_INVALID_REFRESH_TOKEN_EXPIRED(1002001018, "访问令牌已过期"),
OAUTH_INVALID_REFRESH_TOKEN_INVALID(1002001019, "刷新令牌已失效"),
// ========== 管理员模块 1002002000 ========== // ========== 管理员模块 1002002000 ==========
ADMIN_USERNAME_NOT_REGISTERED(1002002000, "账号不存在"), ADMIN_USERNAME_NOT_REGISTERED(1002002000, "账号不存在"),
@ -30,6 +31,8 @@ public enum AdminErrorCodeEnum {
ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"), ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"),
ADMIN_ASSIGN_ROLE_NOT_EXISTS(1002002006, "分配员工角色时,有角色不存在"), ADMIN_ASSIGN_ROLE_NOT_EXISTS(1002002006, "分配员工角色时,有角色不存在"),
ADMIN_INVALID_PERMISSION(1002002007, "没有该操作权限"), ADMIN_INVALID_PERMISSION(1002002007, "没有该操作权限"),
ADMIN_ADMIN_CAN_NOT_UPDATE(1002002008, "管理员的账号不允许变更"),
ADMIN_DEMO_CAN_NOT_WRITE(1002002009, "演示账号暂不允许写操作。欢迎加入我们的交流群http://t.cn/EKEr5WE"),
// ========== 资源模块 1002003000 ========== // ========== 资源模块 1002003000 ==========
RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"), RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"),

View File

@ -8,11 +8,12 @@ import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("OAuth2 创建 Token DTO") @ApiModel("OAuth2 创建 Token DTO")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class OAuth2CreateTokenDTO { public class OAuth2CreateTokenDTO implements Serializable {
@ApiModelProperty(value = "用户编号", required = true, example = "1") @ApiModelProperty(value = "用户编号", required = true, example = "1")
@NotNull(message = "用户编号不能为空") @NotNull(message = "用户编号不能为空")

View File

@ -9,11 +9,12 @@ import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("OAuth2 身份验证 DTO") @ApiModel("OAuth2 身份验证 DTO")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class OAuth2GetTokenDTO { public class OAuth2GetTokenDTO implements Serializable {
@ApiModelProperty(value = "accessToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50") @ApiModelProperty(value = "accessToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
@NotEmpty(message = "accessToken 不能为空") @NotEmpty(message = "accessToken 不能为空")

View File

@ -0,0 +1,28 @@
package cn.iocoder.mall.admin.api.dto.oauth2;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("OAuth2 刷新 Token DTO")
@Data
@Accessors(chain = true)
public class OAuth2RefreshTokenDTO implements Serializable {
@ApiModelProperty(value = "refreshToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
@NotEmpty(message = "refreshToken 不能为空")
private String refreshToken;
@ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 ResourceTypeEnum 枚举")
@NotNull(message = "用户类型不能为空")
@InEnum(value = ResourceTypeEnum.class, message = "用户类型必须是 {value}")
private Integer userType;
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.mall.admin.api.dto.oauth2;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("OAuth2 移除 Token DTO")
@Data
@Accessors(chain = true)
public class OAuth2RemoveTokenByUserDTO implements Serializable {
@ApiModelProperty(value = "用户编号", required = true, example = "1")
@NotNull(message = "用户编号不能为空")
private Integer userId;
@ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 ResourceTypeEnum 枚举")
@NotNull(message = "用户类型不能为空")
@InEnum(value = ResourceTypeEnum.class, message = "用户类型必须是 {value}")
private Integer userType;
}

View File

@ -2,17 +2,12 @@ package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationOldBO;
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.stream.Collectors;
@Mapper @Mapper
public interface OAuth2Convert { public interface OAuth2Convert {
@ -28,15 +23,8 @@ public interface OAuth2Convert {
.setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0)); .setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0));
} }
@Mappings({})
OAuth2AuthenticationOldBO convertToAuthenticationOld(OAuth2AccessTokenDO oauth2AccessTokenDO);
@Mappings({}) @Mappings({})
OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO); OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO);
default OAuth2AuthenticationOldBO convertToAuthenticationOld(OAuth2AccessTokenDO oauth2AccessTokenDO, List<AdminRoleDO> adminRoleDOs) {
return convertToAuthenticationOld(oauth2AccessTokenDO)
.setRoleIds(adminRoleDOs.stream().map(AdminRoleDO::getRoleId).collect(Collectors.toSet()));
}
} }

View File

@ -8,9 +8,16 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface OAuth2AccessTokenMapper extends BaseMapper<OAuth2AccessTokenDO> { public interface OAuth2AccessTokenMapper extends BaseMapper<OAuth2AccessTokenDO> {
default int updateToInvalidByAdminId(Integer adminId) { default int updateToInvalid(Integer userId, Integer userType) {
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>() QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
.eq("admin_id", adminId).eq("valid", true); .eq("user_id", userId).eq("user_type", userType)
.eq("valid", true);
return update(new OAuth2AccessTokenDO().setValid(false), query);
}
default int updateToInvalidByRefreshToken(String refreshToken) {
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
.eq("refresh_token", refreshToken).eq("valid", true);
return update(new OAuth2AccessTokenDO().setValid(false), query); return update(new OAuth2AccessTokenDO().setValid(false), query);
} }

View File

@ -8,9 +8,10 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> { public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
default int updateToInvalidByAdminId(Integer adminId) { default int updateToInvalid(Integer userId, Integer userType) {
QueryWrapper<OAuth2RefreshTokenDO> query = new QueryWrapper<OAuth2RefreshTokenDO>() QueryWrapper<OAuth2RefreshTokenDO> query = new QueryWrapper<OAuth2RefreshTokenDO>()
.eq("admin_id", adminId).eq("valid", true); .eq("user_id", userId).eq("user_type", userType)
.eq("valid", true);
return update(new OAuth2RefreshTokenDO().setValid(false), query); return update(new OAuth2RefreshTokenDO().setValid(false), query);
} }

View File

@ -16,6 +16,7 @@ import cn.iocoder.mall.admin.api.constant.AdminConstants;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.api.dto.admin.*; import cn.iocoder.mall.admin.api.dto.admin.*;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RemoveTokenByUserDTO;
import cn.iocoder.mall.admin.convert.AdminConvert; import cn.iocoder.mall.admin.convert.AdminConvert;
import cn.iocoder.mall.admin.dao.AdminMapper; import cn.iocoder.mall.admin.dao.AdminMapper;
import cn.iocoder.mall.admin.dao.AdminRoleMapper; import cn.iocoder.mall.admin.dao.AdminRoleMapper;
@ -96,9 +97,14 @@ public class AdminServiceImpl implements AdminService {
@Override @Override
public Boolean updateAdmin(Integer adminId, AdminUpdateDTO adminUpdateDTO) { public Boolean updateAdmin(Integer adminId, AdminUpdateDTO adminUpdateDTO) {
// 校验账号存在 // 校验账号存在
if (adminMapper.selectById(adminUpdateDTO.getId()) == null) { AdminDO admin = adminMapper.selectById(adminUpdateDTO.getId());
if (admin == null) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode()); throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
} }
if (AdminConstants.USERNAME_ADMIN.equals(admin.getUsername())
|| AdminConstants.USERNAME_DEMO.equals(admin.getUsername())) { // 特殊账号,不允许编辑
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_ADMIN_CAN_NOT_UPDATE.getCode());
}
// 校验账号唯一 // 校验账号唯一
AdminDO usernameAdmin = adminMapper.selectByUsername(adminUpdateDTO.getUsername()); AdminDO usernameAdmin = adminMapper.selectByUsername(adminUpdateDTO.getUsername());
if (usernameAdmin != null && !usernameAdmin.getId().equals(adminUpdateDTO.getId())) { if (usernameAdmin != null && !usernameAdmin.getId().equals(adminUpdateDTO.getId())) {
@ -120,7 +126,8 @@ public class AdminServiceImpl implements AdminService {
if (admin == null) { if (admin == null) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode()); throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
} }
if (AdminConstants.USERNAME_ADMIN.equals(admin.getUsername())) { if (AdminConstants.USERNAME_ADMIN.equals(admin.getUsername())
|| AdminConstants.USERNAME_DEMO.equals(admin.getUsername())) { // 特殊账号,不允许编辑
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE.getCode()); throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE.getCode());
} }
// 如果状态相同,则返回错误 // 如果状态相同,则返回错误
@ -132,7 +139,7 @@ public class AdminServiceImpl implements AdminService {
adminMapper.updateById(updateAdmin); adminMapper.updateById(updateAdmin);
// 如果是关闭管理员,则标记 token 失效。否则,管理员还可以继续蹦跶 // 如果是关闭管理员,则标记 token 失效。否则,管理员还可以继续蹦跶
if (CommonStatusEnum.DISABLE.getValue().equals(adminUpdateStatusDTO.getStatus())) { if (CommonStatusEnum.DISABLE.getValue().equals(adminUpdateStatusDTO.getStatus())) {
oauth2Service.removeToken(adminUpdateStatusDTO.getId()); oauth2Service.removeToken(new OAuth2RemoveTokenByUserDTO().setUserId(adminId).setUserType(UserTypeEnum.ADMIN.getValue()));
} }
// TODO 插入操作日志 // TODO 插入操作日志
// 返回成功 // 返回成功
@ -234,8 +241,11 @@ public class AdminServiceImpl implements AdminService {
} }
} }
} }
// 获得用户
AdminDO admin = adminMapper.selectById(adminId);
// 返回成功 // 返回成功
return new AdminAuthorizationBO().setId(adminId).setRoleIds(adminRoleIds); return new AdminAuthorizationBO().setId(adminId).setUsername(admin.getUsername())
.setRoleIds(adminRoleIds);
} }
private String encodePassword(String password) { private String encodePassword(String password) {

View File

@ -7,6 +7,8 @@ import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RefreshTokenDTO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RemoveTokenByUserDTO;
import cn.iocoder.mall.admin.convert.OAuth2Convert; import cn.iocoder.mall.admin.convert.OAuth2Convert;
import cn.iocoder.mall.admin.dao.OAuth2AccessTokenMapper; import cn.iocoder.mall.admin.dao.OAuth2AccessTokenMapper;
import cn.iocoder.mall.admin.dao.OAuth2RefreshTokenMapper; import cn.iocoder.mall.admin.dao.OAuth2RefreshTokenMapper;
@ -59,17 +61,37 @@ public class OAuth2ServiceImpl implements OAuth2Service {
return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO); return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO);
} }
/** @Override
* Token
*
* @param adminId
*/
@Transactional @Transactional
public void removeToken(Integer adminId) { public void removeToken(OAuth2RemoveTokenByUserDTO oauth2RemoveTokenByUserDTO) {
Integer userId = oauth2RemoveTokenByUserDTO.getUserId();
Integer userType = oauth2RemoveTokenByUserDTO.getUserType();
// 设置 access token 失效 // 设置 access token 失效
oauth2AccessTokenMapper.updateToInvalidByAdminId(adminId); oauth2AccessTokenMapper.updateToInvalid(userId, userType);
// 设置 refresh token 失效 // 设置 refresh token 失效
oauth2RefreshTokenMapper.updateToInvalidByAdminId(adminId); oauth2RefreshTokenMapper.updateToInvalid(userId, userType);
}
@Override
public OAuth2AccessTokenBO refreshToken(OAuth2RefreshTokenDTO oauth2RefreshTokenDTO) {
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(oauth2RefreshTokenDTO.getRefreshToken());
// 校验刷新令牌是否合法
if (refreshTokenDO == null) { // 不存在
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH_INVALID_REFRESH_TOKEN_NOT_FOUND.getCode());
}
if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH_INVALID_REFRESH_TOKEN_EXPIRED.getCode());
}
if (!refreshTokenDO.getValid()) { // 无效
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH_INVALID_REFRESH_TOKEN_INVALID.getCode());
}
// 标记 refreshToken 对应的 accessToken 都不合法
oauth2AccessTokenMapper.updateToInvalidByRefreshToken(oauth2RefreshTokenDTO.getRefreshToken());
// 创建访问令牌
OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO.getUserId(), refreshTokenDO.getUserType(),
refreshTokenDO.getId());
// 转换返回
return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO);
} }
@Override @Override

View File

@ -1,5 +0,0 @@
/**
* @author Sin
* @time 2019/5/16 10:52 AM
*/
package cn.iocoder.mall.admin;

View File

@ -1,17 +1,16 @@
package cn.iocoder.mall.user.application.controller.users; package cn.iocoder.mall.user.application.controller.users;
import cn.iocoder.common.framework.constant.UserTypeEnum;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.OAuth2Service;
import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2RefreshTokenDTO;
import cn.iocoder.mall.user.api.MobileCodeService; import cn.iocoder.mall.user.api.MobileCodeService;
import cn.iocoder.mall.user.api.OAuth2Service;
import cn.iocoder.mall.user.api.UserService; import cn.iocoder.mall.user.api.UserService;
import cn.iocoder.mall.user.api.bo.OAuth2AccessTokenBO; import cn.iocoder.mall.user.api.bo.user.UserAuthenticationBO;
import cn.iocoder.mall.user.application.convert.PassportConvert; import cn.iocoder.mall.user.api.dto.user.UserAuthenticationByMobileCodeDTO;
import cn.iocoder.mall.user.application.vo.users.UsersAccessTokenVO;
import cn.iocoder.mall.user.application.vo.users.UsersMobileRegisterVO;
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -26,7 +25,7 @@ import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api("Passport 模块") @Api("Passport 模块")
public class PassportController { public class PassportController {
@Reference(validation = "true", version = "${dubbo.provider.OAuth2Service.version}") @Reference(validation = "true", version = "${dubbo.consumer.OAuth2Service.version}")
private OAuth2Service oauth2Service; private OAuth2Service oauth2Service;
@Reference(validation = "true", version = "${dubbo.provider.UserService.version}") @Reference(validation = "true", version = "${dubbo.provider.UserService.version}")
private UserService userService; private UserService userService;
@ -40,20 +39,12 @@ public class PassportController {
// return oauth2Service.getAccessToken(clientId, clientSecret, mobile, password); // return oauth2Service.getAccessToken(clientId, clientSecret, mobile, password);
// } // }
@PermitAll
@PostMapping("/mobile/register") @PostMapping("/mobile/register")
@ApiOperation(value = "手机号 + 验证码登陆(注册)", notes = "如果手机对应的账号不存在,则会自动创建") @ApiOperation(value = "手机号 + 验证码登陆(注册)", notes = "如果手机对应的账号不存在,则会自动创建")
@ApiImplicitParams({ public CommonResult<UserAuthenticationBO> mobileRegister(UserAuthenticationByMobileCodeDTO userAuthenticationByMobileCodeDTO) {
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691300"), return success(userService.authenticationByMobileCode(userAuthenticationByMobileCodeDTO));
@ApiImplicitParam(name = "code", value = "验证码", required = true, example = "9999")
})
public CommonResult<UsersMobileRegisterVO> mobileRegister(@RequestParam("mobile") String mobile,
@RequestParam("code") String code) {
OAuth2AccessTokenBO result = oauth2Service.getAccessToken(mobile, code);
return success(PassportConvert.INSTANCE.convert(result));
} }
@PermitAll
@PostMapping("mobile/send_register_code") @PostMapping("mobile/send_register_code")
@ApiOperation(value = "发送手机验证码") @ApiOperation(value = "发送手机验证码")
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691300") @ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691300")
@ -65,24 +56,21 @@ public class PassportController {
// TODO 芋艿,改绑手机号 // TODO 芋艿,改绑手机号
// TODO 功能qq 登陆 // TODO 功能qq 登陆
@PermitAll
@PostMapping("/qq/login") @PostMapping("/qq/login")
public String qqLogin() { public String qqLogin() {
return null; return null;
} }
// TODO 功能qq 绑定 // TODO 功能qq 绑定
@PermitAll
@PostMapping("/qq/bind") @PostMapping("/qq/bind")
public String qqBind() { public String qqBind() {
return null; return null;
} }
@PermitAll
@PostMapping("/refresh_token") // TODO 功能:刷新 token @PostMapping("/refresh_token") // TODO 功能:刷新 token
public CommonResult<UsersAccessTokenVO> refreshToken(@RequestParam("refreshToken") String refreshToken) { public CommonResult<OAuth2AccessTokenBO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
OAuth2AccessTokenBO result = oauth2Service.refreshToken(refreshToken); return success(oauth2Service.refreshToken(new OAuth2RefreshTokenDTO().setRefreshToken(refreshToken)
return success(PassportConvert.INSTANCE.convert2(result)); .setUserType(UserTypeEnum.USER.getValue())));
} }
// TODO 功能:退出,销毁 token // TODO 功能:退出,销毁 token

View File

@ -6,6 +6,7 @@ import cn.iocoder.mall.user.api.bo.UserBO;
import cn.iocoder.mall.user.api.dto.UserUpdateDTO; import cn.iocoder.mall.user.api.dto.UserUpdateDTO;
import cn.iocoder.mall.user.application.convert.UserConvert; import cn.iocoder.mall.user.application.convert.UserConvert;
import cn.iocoder.mall.user.application.vo.users.UsersUserVO; import cn.iocoder.mall.user.application.vo.users.UsersUserVO;
import cn.iocoder.mall.user.sdk.annotation.RequiresLogin;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -23,6 +24,7 @@ public class UserController {
private UserService userService; private UserService userService;
@GetMapping("/info") @GetMapping("/info")
@RequiresLogin
@ApiOperation(value = "用户信息") @ApiOperation(value = "用户信息")
public CommonResult<UsersUserVO> info() { public CommonResult<UsersUserVO> info() {
UserBO userResult = userService.getUser(UserSecurityContextHolder.getContext().getUserId()); UserBO userResult = userService.getUser(UserSecurityContextHolder.getContext().getUserId());
@ -30,6 +32,7 @@ public class UserController {
} }
@PostMapping("/update_avatar") @PostMapping("/update_avatar")
@RequiresLogin
@ApiOperation(value = "更新头像") @ApiOperation(value = "更新头像")
public CommonResult<Boolean> updateAvatar(@RequestParam("avatar") String avatar) { public CommonResult<Boolean> updateAvatar(@RequestParam("avatar") String avatar) {
// 创建 // 创建
@ -40,6 +43,7 @@ public class UserController {
} }
@PostMapping("/update_nickname") @PostMapping("/update_nickname")
@RequiresLogin
@ApiOperation(value = "更新昵称") @ApiOperation(value = "更新昵称")
public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) { public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) {
// 创建 // 创建

View File

@ -1,21 +0,0 @@
package cn.iocoder.mall.user.application.convert;
import cn.iocoder.mall.user.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.user.application.vo.users.UsersAccessTokenVO;
import cn.iocoder.mall.user.application.vo.users.UsersMobileRegisterVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PassportConvert {
PassportConvert INSTANCE = Mappers.getMapper(PassportConvert.class);
@Mappings({})
UsersMobileRegisterVO convert(OAuth2AccessTokenBO oauth2AccessTokenBO);
@Mappings({})
UsersAccessTokenVO convert2(OAuth2AccessTokenBO result);
}

View File

@ -4,7 +4,9 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable; import java.io.Serializable;
@ -39,13 +41,15 @@ public class UserAddressAddPO implements Serializable {
@NotNull(message = "手机号为不能为空!") @NotNull(message = "手机号为不能为空!")
@Size(min = 11, max = 11, message = "手机号为 11 位!") @Size(min = 11, max = 11, message = "手机号为 11 位!")
private String mobile; private String mobile;
/** /**
* *
*/ */
@ApiModelProperty("收件详细地址") @ApiModelProperty("收件详细地址")
@NotNull(message = "详细地址不能为空") @NotEmpty(message = "详细地址不能为空")
@Size(min = 10, max = 100, message = "地址在 10 ~ 100 字之间!") @Length(min = 10, max = 100, message = "地址在 10 ~ 100 字之间!")
private String address; private String address;
/** /**
* *
*/ */

View File

@ -1,14 +0,0 @@
package cn.iocoder.mall.user.sdk.annotation;
import java.lang.annotation.*;
/**
* URL 访访 URL
*
* 访
*/
@Documented
@Target({ElementType.METHOD}) // ElementType.TYPE 暂时不支持类级别。为了减少判断,略微提升性能。
@Retention(RetentionPolicy.RUNTIME)
public @interface PermitAll {
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.mall.user.sdk.annotation;
import java.lang.annotation.*;
/**
* Controller
*
* 访 API
* 1. 线 @RequiresLogin
* 2. API
*/
@Documented
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresLogin {
}

View File

@ -1,18 +1,18 @@
package cn.iocoder.mall.user.sdk.context; package cn.iocoder.mall.user.sdk.context;
import lombok.Data;
import lombok.experimental.Accessors;
/** /**
* User Security * User Security
*/ */
@Data
@Accessors(chain = true)
public class UserSecurityContext { public class UserSecurityContext {
private final Integer userId; /**
*
public UserSecurityContext(Integer userId) { */
this.userId = userId; private Integer userId;
}
public Integer getUserId() {
return userId;
}
} }

View File

@ -17,7 +17,7 @@ public class UserSecurityContextHolder {
UserSecurityContext ctx = SECURITY_CONTEXT.get(); UserSecurityContext ctx = SECURITY_CONTEXT.get();
// 为空时,设置一个空的进去 // 为空时,设置一个空的进去
if (ctx == null) { if (ctx == null) {
ctx = new UserSecurityContext(null); ctx = new UserSecurityContext();
SECURITY_CONTEXT.set(ctx); SECURITY_CONTEXT.set(ctx);
} }
return ctx; return ctx;

View File

@ -1,12 +1,15 @@
package cn.iocoder.mall.user.sdk.interceptor; package cn.iocoder.mall.user.sdk.interceptor;
import cn.iocoder.common.framework.constant.MallConstants; import cn.iocoder.common.framework.constant.UserTypeEnum;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.util.MallUtil; import cn.iocoder.common.framework.util.MallUtil;
import cn.iocoder.mall.user.api.OAuth2Service; import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.mall.user.api.bo.OAuth2AuthenticationBO; import cn.iocoder.mall.admin.api.OAuth2Service;
import cn.iocoder.mall.user.sdk.annotation.PermitAll; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO;
import cn.iocoder.mall.user.sdk.annotation.RequiresLogin;
import cn.iocoder.mall.user.sdk.context.UserSecurityContext; import cn.iocoder.mall.user.sdk.context.UserSecurityContext;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
@ -18,40 +21,55 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
/** /**
* * User
*/ */
@Component @Component
public class UserSecurityInterceptor extends HandlerInterceptorAdapter { public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
@Reference(validation = "true", version = "${dubbo.provider.OAuth2Service.version:1.0.0}") @Reference(validation = "true", version = "${dubbo.consumer.OAuth2Service.version:1.0.0}")
private OAuth2Service oauth2Service; private OAuth2Service oauth2Service;
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 设置当前访问的用户类型。注意,即使未登陆,我们也认为是用户 // 设置当前访问的用户类型。注意,即使未登陆,我们也认为是用户
MallUtil.setUserType(request, MallConstants.USER_TYPE_USER); MallUtil.setUserType(request, UserTypeEnum.USER.getValue());
// 校验访问令牌是否正确。若正确,返回授权信息
// 根据 accessToken 获得认证信息,判断是谁
String accessToken = HttpUtil.obtainAuthorization(request); String accessToken = HttpUtil.obtainAuthorization(request);
OAuth2AuthenticationBO authentication = null; OAuth2AuthenticationBO authentication = null;
if (accessToken != null) { ServiceException serviceException = null;
authentication = oauth2Service.checkToken(accessToken); // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常 if (StringUtil.hasText(accessToken)) {
// 添加到 SecurityContext try {
UserSecurityContext context = new UserSecurityContext(authentication.getUserId()); authentication = oauth2Service.getAuthentication(new OAuth2GetTokenDTO().setAccessToken(accessToken)
UserSecurityContextHolder.setContext(context); .setUserType(UserTypeEnum.USER.getValue()));
// 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为: } catch (ServiceException e) {
// AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全 serviceException = e;
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
// 因此,这里需要进行记录
if (authentication.getUserId() != null) {
MallUtil.setUserId(request, authentication.getUserId());
} }
} }
// 校验是否需要已授权
// 进行鉴权
HandlerMethod method = (HandlerMethod) handler; HandlerMethod method = (HandlerMethod) handler;
boolean isPermitAll = method.hasMethodAnnotation(PermitAll.class); boolean requiresLogin = method.hasMethodAnnotation(RequiresLogin.class);
if (!isPermitAll && authentication == null) { if (requiresLogin) { // 如果需要鉴权
throw new ServiceException(-1, "未授权"); // TODO 这里要改下 if (serviceException != null) { // 认证失败,抛出上面认证失败的 ServiceException 异常
throw serviceException;
}
if (authentication == null) { // 无认证信息,抛出未登陆 ServiceException 异常
throw new ServiceException(AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getMessage());
}
// TODO 芋艿,后续拓展读取用户信息
} }
// 鉴权完成,初始化 AdminSecurityContext 上下文
UserSecurityContext context = new UserSecurityContext();
UserSecurityContextHolder.setContext(context);
if (authentication != null) {
context.setUserId(authentication.getUserId());
MallUtil.setUserId(request, authentication.getUserId()); // 记录到 request 中,避免 AdminSecurityContext 后续清理掉后,其它地方需要用到 userId
// TODO 芋艿,后续拓展读取用户信息
}
// 返回成功
return super.preHandle(request, response, handler); return super.preHandle(request, response, handler);
} }

View File

@ -17,6 +17,11 @@
<artifactId>common-framework</artifactId> <artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 工具类相关 --> <!-- 工具类相关 -->
<dependency> <dependency>

Some files were not shown because too many files have changed in this diff Show More