parent
21dbd7b8cb
commit
58525e0940
|
@ -0,0 +1,4 @@
|
|||
*.css linguist-language=java
|
||||
*.less linguist-language=java
|
||||
*.js linguist-language=java
|
||||
*.html linguist-language=java
|
30
README.md
30
README.md
|
@ -33,9 +33,35 @@ TODO 暂时不提供管理后台的账号密码,等后面提供。
|
|||
|
||||
TODO 此处应有一个演示的装逼 GIF 图。
|
||||
|
||||
## TODO
|
||||
## 其它演示
|
||||
|
||||
提供其他演示环境。例如说 skywalking、sentinel 等等。
|
||||
下面,我们会提供目前用到的中间件的管理平台。
|
||||
|
||||
> 艿艿:考虑到大家可以看到更全的功能,所以一般提供 admin 账号。所以,大家素质使用哟。
|
||||
|
||||
** SkyWalking UI **
|
||||
|
||||
* 地址:http://skywalking-ui.shop.iocoder.cn:18099
|
||||
* 管理员账号:admin / admin
|
||||
|
||||
** Dubbo Admin **
|
||||
|
||||
* 地址:http://dubbo-admin.shop.iocoder.cn:18099
|
||||
* 管理员账号:无需登陆
|
||||
|
||||
** RocketMQ Console **
|
||||
|
||||
* 地址:http://rocketmq-console.shop.iocoder.cn:18099
|
||||
* 管理员账号:admin / RPsa2GHjTNs8pxEU
|
||||
|
||||
** Sentinel Console **
|
||||
|
||||
TODO
|
||||
|
||||
** XXL-Job Console **
|
||||
|
||||
* 地址:http://job-console.shop.iocoder.cn:18099
|
||||
* 管理员账号:admin / 233666
|
||||
|
||||
# 技术
|
||||
|
||||
|
|
18
Server.md
18
Server.md
|
@ -1,18 +0,0 @@
|
|||
# 前端 Server
|
||||
|
||||
* admin 18083
|
||||
*
|
||||
|
||||
# 后端 Server
|
||||
|
||||
# 基础服务
|
||||
|
||||
## MySQL
|
||||
|
||||
## Zookeeper
|
||||
|
||||
# 运维
|
||||
|
||||
* ssh 端口
|
||||
* 工作目录 /work2/
|
||||
|
|
@ -54,4 +54,6 @@ public class GlobalExceptionHandler {
|
|||
return CommonResult.error(SysErrorCodeEnum.SYS_ERROR.getCode(), SysErrorCodeEnum.SYS_ERROR.getMessage());
|
||||
}
|
||||
|
||||
// TODO 芋艿,应该还有其它的异常,需要进行翻译
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package cn.iocoder.common.framework.dubbo;
|
||||
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.*;
|
||||
import org.apache.dubbo.rpc.filter.ExceptionFilter;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 基于 {@link org.apache.dubbo.rpc.filter.ExceptionFilter} 实现
|
||||
*
|
||||
* 主要目的是,一些全局性的异常,能够返回。因为,Dubbo Consumer 能够保证,一定会引入全局性的异常。
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER)
|
||||
public class DubboExceptionFilter implements Filter {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public DubboExceptionFilter() {
|
||||
this(LoggerFactory.getLogger(ExceptionFilter.class));
|
||||
}
|
||||
|
||||
public DubboExceptionFilter(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
try {
|
||||
return invoker.invoke(invocation);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
if (result.hasException() && GenericService.class != invoker.getInterface()) {
|
||||
try {
|
||||
Throwable exception = result.getException();
|
||||
|
||||
// directly throw if it's checked exception
|
||||
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
|
||||
return result;
|
||||
} else if (exception instanceof ServiceException) { // add by 芋艿。如果是业务异常,继续抛出
|
||||
return result;
|
||||
}
|
||||
// directly throw if the exception appears in the signature
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
|
||||
Class<?>[] exceptionClassses = method.getExceptionTypes();
|
||||
for (Class<?> exceptionClass : exceptionClassses) {
|
||||
if (exception.getClass().equals(exceptionClass)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// for the exception not found in method's signature, print ERROR message in server's log.
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
|
||||
|
||||
// directly throw if exception class and interface class are in the same jar file.
|
||||
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
|
||||
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
|
||||
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
|
||||
return result;
|
||||
}
|
||||
// directly throw if it's JDK exception
|
||||
String className = exception.getClass().getName();
|
||||
if (className.startsWith("java.") || className.startsWith("javax.")) {
|
||||
return result;
|
||||
}
|
||||
// directly throw if it's dubbo exception
|
||||
if (exception instanceof RpcException) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// otherwise, wrap with RuntimeException and throw back to the client
|
||||
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
|
||||
} catch (Throwable e) {
|
||||
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ package cn.iocoder.common.framework.exception;
|
|||
* 不限制规则。
|
||||
* 一般建议,每个模块自增。
|
||||
*/
|
||||
public class ServiceException extends RuntimeException {
|
||||
public final class ServiceException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
|
@ -43,4 +43,4 @@ public class ServiceException extends RuntimeException {
|
|||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
dubboExceptionFilter=cn.iocoder.common.framework.dubbo.DubboExceptionFilter
|
|
@ -1,21 +0,0 @@
|
|||
package cn.iocoder.mall.order.api.bo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品分组 BO
|
||||
*
|
||||
* 主要目的是,多个商品,
|
||||
*/
|
||||
public class CartItemGroupBO {
|
||||
|
||||
/**
|
||||
* TODO 芋艿,活动
|
||||
*/
|
||||
private Object activity;
|
||||
/**
|
||||
* 商品数组
|
||||
*/
|
||||
private List<CartItemBO> items;
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package cn.iocoder.mall.order.api.bo;
|
||||
|
||||
public class FeeMessageBO {
|
||||
|
||||
/**
|
||||
* 总价
|
||||
*/
|
||||
private Integer originalTotal;
|
||||
/**
|
||||
* 优惠总价
|
||||
*
|
||||
* 注意,满多少元包邮,不算在优惠中。
|
||||
*/
|
||||
private Integer discountTotal;
|
||||
/**
|
||||
* 邮费
|
||||
*/
|
||||
private Integer postageTotal;
|
||||
/**
|
||||
* 最终价格
|
||||
*
|
||||
* 计算公式 = 总价 - 优惠总价 + 邮费
|
||||
*/
|
||||
private Integer presentTotal;
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
|
@ -24,6 +24,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.order.biz.service
|
||||
provider:
|
||||
filter: -exception
|
||||
CartService:
|
||||
version: 1.0.0
|
||||
consumer:
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
# xxl-job
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://127.0.0.1:18079/
|
||||
executor:
|
||||
appname: pay-job-executor
|
||||
ip:
|
||||
port: 0
|
||||
logpath: /Users/yunai/logs/xxl-job/
|
||||
logretentiondays: 1
|
||||
accessToken:
|
|
@ -0,0 +1,26 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_apy?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
||||
|
||||
# xxl-job
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://127.0.0.1:18079/
|
||||
executor:
|
||||
appname: pay-job-executor
|
||||
ip:
|
||||
port: 0
|
||||
logpath: /Users/yunai/logs/xxl-job/
|
||||
logretentiondays: 1
|
||||
accessToken:
|
||||
|
||||
# rocketmq
|
||||
rocketmq:
|
||||
name-server: 192.168.88.14:9876
|
||||
producer:
|
||||
group: pay-producer-group
|
|
@ -24,6 +24,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.pay.biz.service
|
||||
provider:
|
||||
filter: -exception
|
||||
PayTransactionService:
|
||||
version: 1.0.0
|
||||
PayRefundService:
|
||||
|
|
|
@ -6,6 +6,8 @@ import lombok.experimental.Accessors;
|
|||
|
||||
/**
|
||||
* 商品 SPU
|
||||
*
|
||||
* TODO 芋艿,后面增加商品普通参数。例如说,正面材料,背面材料,屏幕尺寸。
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
||||
|
||||
# rocketmq
|
||||
rocketmq:
|
||||
name-server: 192.168.88.14:9876
|
||||
producer:
|
||||
group: product-producer-group
|
|
@ -24,6 +24,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.product.service
|
||||
provider:
|
||||
filter: -exception
|
||||
ProductAttrService:
|
||||
version: 1.0.0
|
||||
ProductCategoryService:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
|
@ -27,6 +27,7 @@ dubbo:
|
|||
ProductSpuService:
|
||||
version: 1.0.0
|
||||
provider:
|
||||
filter: -exception
|
||||
BannerService:
|
||||
version: 1.0.0
|
||||
CouponService:
|
||||
|
|
|
@ -19,6 +19,9 @@ import java.util.Collections;
|
|||
|
||||
import static cn.iocoder.common.framework.vo.CommonResult.success;
|
||||
|
||||
// TODO 芋艿,搜索关键字的配置
|
||||
// TODO 芋艿,搜索日志
|
||||
|
||||
@RestController
|
||||
@RequestMapping("users/product")
|
||||
@Api("商品搜索")
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# es
|
||||
spring:
|
||||
data:
|
||||
elasticsearch:
|
||||
cluster-name: elasticsearch
|
||||
cluster-nodes: 192.168.88.14:9300
|
||||
repositories:
|
||||
enable: true
|
||||
|
||||
# rocketmq
|
||||
rocketmq:
|
||||
name-server: 192.168.88.14:9876
|
||||
producer:
|
||||
group: search-producer-group
|
|
@ -19,6 +19,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.search.biz.service
|
||||
provider:
|
||||
filter: -exception
|
||||
ProductSearchService:
|
||||
version: 1.0.0
|
||||
consumer:
|
||||
|
|
|
@ -50,7 +50,6 @@ public class AdminController {
|
|||
public CommonResult<List<AdminMenuTreeNodeVO>> menuResourceTree() {
|
||||
List<ResourceBO> resources = resourceService.getResourcesByTypeAndRoleIds(ResourceConstants.TYPE_MENU, AdminSecurityContextHolder.getContext().getRoleIds());
|
||||
// 创建 AdminMenuTreeNodeVO Map
|
||||
// Map<Integer, AdminMenuTreeNodeVO> treeNodeMap = resources.stream().collect(Collectors.toMap(ResourceBO::getId, ResourceConvert.INSTANCE::convert));
|
||||
Map<Integer, AdminMenuTreeNodeVO> treeNodeMap = new LinkedHashMap<>(); // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
|
||||
resources.stream().sorted(Comparator.comparing(ResourceBO::getSort)).forEach(resourceBO -> treeNodeMap.put(resourceBO.getId(), ResourceConvert.INSTANCE.convert(resourceBO)));
|
||||
// 处理父子关系
|
||||
|
|
|
@ -2,7 +2,7 @@ spring:
|
|||
boot:
|
||||
admin:
|
||||
client:
|
||||
enabled: true
|
||||
enabled: false # 暂时不用了
|
||||
url: http://127.0.0.1:18097
|
||||
|
||||
|
||||
|
@ -12,4 +12,4 @@ management:
|
|||
exposure:
|
||||
include: "*"
|
||||
server:
|
||||
port: 19083 # 配置独立端口。而该端口,不使用 nginx 对外暴露,从而不配置安全认证。也就是说,内网环境可访问,外网环境不可访问。当然,这么做的前提是,认为内网安全。
|
||||
port: 19083 # 配置独立端口。而该端口,不使用 nginx 对外暴露,从而不配置安全认证。也就是说,内网环境可访问,外网环境不可访问。当然,这么做的前提是,认为内网安全。
|
||||
|
|
|
@ -7,24 +7,24 @@ package cn.iocoder.mall.admin.sdk.context;
|
|||
*/
|
||||
public class AdminSecurityContextHolder {
|
||||
|
||||
private static final ThreadLocal<AdminSecurityContext> securityContext = new ThreadLocal<AdminSecurityContext>();
|
||||
private static final ThreadLocal<AdminSecurityContext> SECURITY_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void setContext(AdminSecurityContext context) {
|
||||
securityContext.set(context);
|
||||
SECURITY_CONTEXT.set(context);
|
||||
}
|
||||
|
||||
public static AdminSecurityContext getContext() {
|
||||
AdminSecurityContext ctx = securityContext.get();
|
||||
AdminSecurityContext ctx = SECURITY_CONTEXT.get();
|
||||
// 为空时,设置一个空的进去
|
||||
if (ctx == null) {
|
||||
ctx = new AdminSecurityContext(null, null);
|
||||
securityContext.set(ctx);
|
||||
SECURITY_CONTEXT.set(ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
securityContext.remove();
|
||||
SECURITY_CONTEXT.remove();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,4 +34,7 @@ public class AdminDO extends DeletableDO {
|
|||
*/
|
||||
private Integer status;
|
||||
|
||||
// TODO 芋艿,最后登陆时间、最后登陆 IP
|
||||
// TODO 芋艿,登陆日志
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_admin?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
|
@ -24,6 +24,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.admin.service
|
||||
provider:
|
||||
filter: -exception
|
||||
AdminAccessLogService:
|
||||
version: 1.0.0
|
||||
AdminService:
|
||||
|
|
|
@ -7,24 +7,24 @@ package cn.iocoder.mall.user.sdk.context;
|
|||
*/
|
||||
public class UserSecurityContextHolder {
|
||||
|
||||
private static final ThreadLocal<UserSecurityContext> securityContext = new ThreadLocal<UserSecurityContext>();
|
||||
private static final ThreadLocal<UserSecurityContext> SECURITY_CONTEXT = new ThreadLocal<UserSecurityContext>();
|
||||
|
||||
public static void setContext(UserSecurityContext context) {
|
||||
securityContext.set(context);
|
||||
SECURITY_CONTEXT.set(context);
|
||||
}
|
||||
|
||||
public static UserSecurityContext getContext() {
|
||||
UserSecurityContext ctx = securityContext.get();
|
||||
UserSecurityContext ctx = SECURITY_CONTEXT.get();
|
||||
// 为空时,设置一个空的进去
|
||||
if (ctx == null) {
|
||||
ctx = new UserSecurityContext(null);
|
||||
securityContext.set(ctx);
|
||||
SECURITY_CONTEXT.set(ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
securityContext.remove();
|
||||
SECURITY_CONTEXT.remove();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
spring:
|
||||
# datasource
|
||||
datasource:
|
||||
url: jdbc:mysql://192.168.88.14:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
username: root
|
||||
password: ${MALL_MYSQL_PASSWORD}
|
|
@ -24,6 +24,7 @@ dubbo:
|
|||
scan:
|
||||
base-packages: cn.iocoder.mall.user.biz.service
|
||||
provider:
|
||||
filter: -exception
|
||||
MobileCodeService:
|
||||
version: 1.0.0
|
||||
OAuth2Service:
|
||||
|
|
Loading…
Reference in New Issue