From 87b743ae110bab9d06a90741b7d4a6e484f04f16 Mon Sep 17 00:00:00 2001 From: OpenCode Date: Fri, 24 Jul 2026 09:31:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E7=BB=84=E7=BB=87=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20-=20=E5=AE=9E=E4=BD=93/Service/Controller/=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E5=99=A8/=E7=A7=9F=E6=88=B7=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/request/RequestParamsFilter.java | 13 +- .../cool/core/security/AppOrgAuthFilter.java | 92 +++++++++ .../cool/core/security/JwtSecurityConfig.java | 3 + .../admin/AdminFruitGoodsController.java | 22 +++ .../app/AppFruitGoodsController.java | 34 ++++ .../fruit/entity/FruitGoodsEntity.java | 34 ++++ .../fruit/mapper/FruitGoodsMapper.java | 7 + .../fruit/service/FruitGoodsService.java | 7 + .../service/impl/FruitGoodsServiceImpl.java | 12 ++ .../admin/AdminOrderInfoController.java | 22 +++ .../app/AppOrderInfoController.java | 33 ++++ .../modules/order/entity/OrderInfoEntity.java | 29 +++ .../modules/order/mapper/OrderInfoMapper.java | 7 + .../order/service/OrderInfoService.java | 7 + .../service/impl/OrderInfoServiceImpl.java | 12 ++ .../admin/AdminAppModuleController.java | 23 +++ .../admin/AdminAppOrgController.java | 54 +++++ .../admin/AdminAppOrgModuleController.java | 22 +++ .../admin/AdminAppOrgUserController.java | 50 +++++ .../org/controller/app/AppOrgController.java | 70 +++++++ .../controller/app/AppOrgUserController.java | 44 +++++ .../modules/org/entity/AppModuleEntity.java | 39 ++++ .../cool/modules/org/entity/AppOrgEntity.java | 42 ++++ .../org/entity/AppOrgModuleEntity.java | 29 +++ .../modules/org/entity/AppOrgUserEntity.java | 44 +++++ .../modules/org/mapper/AppModuleMapper.java | 7 + .../cool/modules/org/mapper/AppOrgMapper.java | 7 + .../org/mapper/AppOrgModuleMapper.java | 7 + .../modules/org/mapper/AppOrgUserMapper.java | 7 + .../modules/org/service/AppModuleService.java | 7 + .../org/service/AppOrgModuleService.java | 12 ++ .../modules/org/service/AppOrgService.java | 16 ++ .../org/service/AppOrgUserService.java | 25 +++ .../service/impl/AppModuleServiceImpl.java | 12 ++ .../service/impl/AppOrgModuleServiceImpl.java | 35 ++++ .../org/service/impl/AppOrgServiceImpl.java | 186 ++++++++++++++++++ .../service/impl/AppOrgUserServiceImpl.java | 180 +++++++++++++++++ .../admin/AdminZooAnimalController.java | 22 +++ .../app/AppZooAnimalController.java | 33 ++++ .../modules/zoo/entity/ZooAnimalEntity.java | 31 +++ .../modules/zoo/mapper/ZooAnimalMapper.java | 7 + .../modules/zoo/service/ZooAnimalService.java | 7 + .../service/impl/ZooAnimalServiceImpl.java | 12 ++ src/main/resources/application.yml | 2 +- 44 files changed, 1364 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/cool/core/security/AppOrgAuthFilter.java create mode 100644 src/main/java/com/cool/modules/fruit/controller/admin/AdminFruitGoodsController.java create mode 100644 src/main/java/com/cool/modules/fruit/controller/app/AppFruitGoodsController.java create mode 100644 src/main/java/com/cool/modules/fruit/entity/FruitGoodsEntity.java create mode 100644 src/main/java/com/cool/modules/fruit/mapper/FruitGoodsMapper.java create mode 100644 src/main/java/com/cool/modules/fruit/service/FruitGoodsService.java create mode 100644 src/main/java/com/cool/modules/fruit/service/impl/FruitGoodsServiceImpl.java create mode 100644 src/main/java/com/cool/modules/order/controller/admin/AdminOrderInfoController.java create mode 100644 src/main/java/com/cool/modules/order/controller/app/AppOrderInfoController.java create mode 100644 src/main/java/com/cool/modules/order/entity/OrderInfoEntity.java create mode 100644 src/main/java/com/cool/modules/order/mapper/OrderInfoMapper.java create mode 100644 src/main/java/com/cool/modules/order/service/OrderInfoService.java create mode 100644 src/main/java/com/cool/modules/order/service/impl/OrderInfoServiceImpl.java create mode 100644 src/main/java/com/cool/modules/org/controller/admin/AdminAppModuleController.java create mode 100644 src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgController.java create mode 100644 src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgModuleController.java create mode 100644 src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgUserController.java create mode 100644 src/main/java/com/cool/modules/org/controller/app/AppOrgController.java create mode 100644 src/main/java/com/cool/modules/org/controller/app/AppOrgUserController.java create mode 100644 src/main/java/com/cool/modules/org/entity/AppModuleEntity.java create mode 100644 src/main/java/com/cool/modules/org/entity/AppOrgEntity.java create mode 100644 src/main/java/com/cool/modules/org/entity/AppOrgModuleEntity.java create mode 100644 src/main/java/com/cool/modules/org/entity/AppOrgUserEntity.java create mode 100644 src/main/java/com/cool/modules/org/mapper/AppModuleMapper.java create mode 100644 src/main/java/com/cool/modules/org/mapper/AppOrgMapper.java create mode 100644 src/main/java/com/cool/modules/org/mapper/AppOrgModuleMapper.java create mode 100644 src/main/java/com/cool/modules/org/mapper/AppOrgUserMapper.java create mode 100644 src/main/java/com/cool/modules/org/service/AppModuleService.java create mode 100644 src/main/java/com/cool/modules/org/service/AppOrgModuleService.java create mode 100644 src/main/java/com/cool/modules/org/service/AppOrgService.java create mode 100644 src/main/java/com/cool/modules/org/service/AppOrgUserService.java create mode 100644 src/main/java/com/cool/modules/org/service/impl/AppModuleServiceImpl.java create mode 100644 src/main/java/com/cool/modules/org/service/impl/AppOrgModuleServiceImpl.java create mode 100644 src/main/java/com/cool/modules/org/service/impl/AppOrgServiceImpl.java create mode 100644 src/main/java/com/cool/modules/org/service/impl/AppOrgUserServiceImpl.java create mode 100644 src/main/java/com/cool/modules/zoo/controller/admin/AdminZooAnimalController.java create mode 100644 src/main/java/com/cool/modules/zoo/controller/app/AppZooAnimalController.java create mode 100644 src/main/java/com/cool/modules/zoo/entity/ZooAnimalEntity.java create mode 100644 src/main/java/com/cool/modules/zoo/mapper/ZooAnimalMapper.java create mode 100644 src/main/java/com/cool/modules/zoo/service/ZooAnimalService.java create mode 100644 src/main/java/com/cool/modules/zoo/service/impl/ZooAnimalServiceImpl.java diff --git a/src/main/java/com/cool/core/request/RequestParamsFilter.java b/src/main/java/com/cool/core/request/RequestParamsFilter.java index 89ae06b..95301e1 100644 --- a/src/main/java/com/cool/core/request/RequestParamsFilter.java +++ b/src/main/java/com/cool/core/request/RequestParamsFilter.java @@ -1,5 +1,6 @@ package com.cool.core.request; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; @@ -81,7 +82,17 @@ public class RequestParamsFilter implements Filter { } } // app端,userId 为当前登录的用户id - case APP -> requestParams.set("userId", CoolSecurityUtil.getCurrentUserId()); + case APP -> { + requestParams.set("userId", CoolSecurityUtil.getCurrentUserId()); + // App端:从tokenInfo中读取orgId作为tenantId,实现组织级数据隔离 + Object jwtObj = requestParams.get("tokenInfo"); + if (jwtObj instanceof Map) { + Object orgId = ((Map) jwtObj).get("orgId"); + if (ObjUtil.isNotNull(orgId)) { + requestParams.set("tenantId", Convert.toLong(orgId)); + } + } + } } return CoolSecurityUtil.getTenantId(requestParams); } diff --git a/src/main/java/com/cool/core/security/AppOrgAuthFilter.java b/src/main/java/com/cool/core/security/AppOrgAuthFilter.java new file mode 100644 index 0000000..ddd26c9 --- /dev/null +++ b/src/main/java/com/cool/core/security/AppOrgAuthFilter.java @@ -0,0 +1,92 @@ +package com.cool.core.security; + +import cn.hutool.core.util.StrUtil; +import com.cool.core.exception.CoolException; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.org.entity.AppOrgEntity; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import com.cool.modules.org.entity.AppOrgUserEntity; +import com.cool.modules.org.service.AppOrgModuleService; +import com.cool.modules.org.service.AppOrgUserService; +import com.mybatisflex.core.query.QueryWrapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@Component +@Order(3) +@RequiredArgsConstructor +public class AppOrgAuthFilter extends OncePerRequestFilter { + + private final AppOrgUserService appOrgUserService; + private final AppOrgModuleService appOrgModuleService; + + private static final Map MODULE_PATH_MAP = Map.of( + "/app/fruit/", "fruit", + "/app/zoo/", "zoo", + "/app/order/", "order", + "/app/book/", "book", + "/app/ticket/", "ticket" + ); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain chain) throws ServletException, IOException { + String uri = request.getRequestURI(); + + if (!uri.startsWith("/app/") || uri.startsWith("/app/org/") || uri.startsWith("/app/user/") || uri.startsWith("/app/base/") || uri.startsWith("/app/dict/")) { + chain.doFilter(request, response); + return; + } + + if (com.cool.core.enums.UserTypeEnum.APP != CoolSecurityUtil.getCurrentUserType()) { + chain.doFilter(request, response); + return; + } + + String moduleKey = resolveModuleKey(uri); + if (moduleKey == null) { + chain.doFilter(request, response); + return; + } + + Long userId = CoolSecurityUtil.getCurrentUserId(); + String orgIdStr = request.getHeader("X-Org-Id"); + if (StrUtil.isBlank(orgIdStr)) { + throw new CoolException("缺少组织信息", 403); + } + + Long orgId; + try { + orgId = Long.parseLong(orgIdStr); + } catch (NumberFormatException e) { + throw new CoolException("无效的组织ID", 403); + } + + if (!appOrgUserService.isOrgMember(userId, orgId)) { + throw new CoolException("未加入该组织", 403); + } + + if (!appOrgModuleService.isModuleAuthorized(orgId, moduleKey)) { + throw new CoolException("该组织未授权此模块", 403); + } + + chain.doFilter(request, response); + } + + private String resolveModuleKey(String uri) { + for (Map.Entry entry : MODULE_PATH_MAP.entrySet()) { + if (uri.startsWith(entry.getKey())) { + return entry.getValue(); + } + } + return null; + } +} diff --git a/src/main/java/com/cool/core/security/JwtSecurityConfig.java b/src/main/java/com/cool/core/security/JwtSecurityConfig.java index c49be0b..60efe73 100644 --- a/src/main/java/com/cool/core/security/JwtSecurityConfig.java +++ b/src/main/java/com/cool/core/security/JwtSecurityConfig.java @@ -43,6 +43,7 @@ public class JwtSecurityConfig { // 用户详情 final private UserDetailsService userDetailsService; final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + final private AppOrgAuthFilter appOrgAuthFilter; // 401 final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler; // 403 @@ -72,6 +73,8 @@ public class JwtSecurityConfig { .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterAfter(appOrgAuthFilter, + JwtAuthenticationTokenFilter.class) .exceptionHandling(config -> { config.authenticationEntryPoint(entryPointUnauthorizedHandler); config.accessDeniedHandler(restAccessDeniedHandler); diff --git a/src/main/java/com/cool/modules/fruit/controller/admin/AdminFruitGoodsController.java b/src/main/java/com/cool/modules/fruit/controller/admin/AdminFruitGoodsController.java new file mode 100644 index 0000000..e420c34 --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/controller/admin/AdminFruitGoodsController.java @@ -0,0 +1,22 @@ +package com.cool.modules.fruit.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.fruit.entity.FruitGoodsEntity; +import com.cool.modules.fruit.service.FruitGoodsService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "水果管理", description = "水果商品管理") +@CoolRestController(api = {"add", "delete", "update", "page", "info"}) +public class AdminFruitGoodsController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } +} diff --git a/src/main/java/com/cool/modules/fruit/controller/app/AppFruitGoodsController.java b/src/main/java/com/cool/modules/fruit/controller/app/AppFruitGoodsController.java new file mode 100644 index 0000000..776084b --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/controller/app/AppFruitGoodsController.java @@ -0,0 +1,34 @@ +package com.cool.modules.fruit.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.request.R; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.fruit.entity.FruitGoodsEntity; +import com.cool.modules.fruit.service.FruitGoodsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "水果", description = "App端水果") +@CoolRestController +public class AppFruitGoodsController { + + private final FruitGoodsService fruitGoodsService; + + @Operation(summary = "水果列表") + @GetMapping("/list") + public R list(@RequestAttribute JSONObject requestParams) { + return R.ok(fruitGoodsService.list(requestParams, null)); + } + + @Operation(summary = "水果详情") + @GetMapping("/detail") + public R detail(@RequestAttribute JSONObject requestParams) { + Long id = requestParams.getLong("id"); + return R.ok(fruitGoodsService.info(id)); + } +} diff --git a/src/main/java/com/cool/modules/fruit/entity/FruitGoodsEntity.java b/src/main/java/com/cool/modules/fruit/entity/FruitGoodsEntity.java new file mode 100644 index 0000000..207b8df --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/entity/FruitGoodsEntity.java @@ -0,0 +1,34 @@ +package com.cool.modules.fruit.entity; + +import com.cool.core.base.TenantEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "fruit_goods", comment = "水果商品表") +public class FruitGoodsEntity extends TenantEntity { + + @ColumnDefine(comment = "商品名称", notNull = true) + private String name; + + @ColumnDefine(comment = "价格") + private Double price; + + @ColumnDefine(comment = "单位") + private String unit; + + @ColumnDefine(comment = "图片") + private String image; + + @ColumnDefine(comment = "描述", type = "text") + private String description; + + @ColumnDefine(comment = "分类ID", type = "bigint") + private Long categoryId; + + @ColumnDefine(comment = "状态 0:下架 1:上架", defaultValue = "1") + private Integer status; +} diff --git a/src/main/java/com/cool/modules/fruit/mapper/FruitGoodsMapper.java b/src/main/java/com/cool/modules/fruit/mapper/FruitGoodsMapper.java new file mode 100644 index 0000000..505215a --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/mapper/FruitGoodsMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.fruit.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.fruit.entity.FruitGoodsEntity; + +public interface FruitGoodsMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/fruit/service/FruitGoodsService.java b/src/main/java/com/cool/modules/fruit/service/FruitGoodsService.java new file mode 100644 index 0000000..880372a --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/service/FruitGoodsService.java @@ -0,0 +1,7 @@ +package com.cool.modules.fruit.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.fruit.entity.FruitGoodsEntity; + +public interface FruitGoodsService extends BaseService { +} diff --git a/src/main/java/com/cool/modules/fruit/service/impl/FruitGoodsServiceImpl.java b/src/main/java/com/cool/modules/fruit/service/impl/FruitGoodsServiceImpl.java new file mode 100644 index 0000000..baaf5a5 --- /dev/null +++ b/src/main/java/com/cool/modules/fruit/service/impl/FruitGoodsServiceImpl.java @@ -0,0 +1,12 @@ +package com.cool.modules.fruit.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.fruit.entity.FruitGoodsEntity; +import com.cool.modules.fruit.mapper.FruitGoodsMapper; +import com.cool.modules.fruit.service.FruitGoodsService; +import org.springframework.stereotype.Service; + +@Service +public class FruitGoodsServiceImpl extends BaseServiceImpl implements + FruitGoodsService { +} diff --git a/src/main/java/com/cool/modules/order/controller/admin/AdminOrderInfoController.java b/src/main/java/com/cool/modules/order/controller/admin/AdminOrderInfoController.java new file mode 100644 index 0000000..7f12d35 --- /dev/null +++ b/src/main/java/com/cool/modules/order/controller/admin/AdminOrderInfoController.java @@ -0,0 +1,22 @@ +package com.cool.modules.order.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.order.entity.OrderInfoEntity; +import com.cool.modules.order.service.OrderInfoService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "订单管理", description = "订单信息管理") +@CoolRestController(api = {"delete", "update", "page", "info"}, cname = "orderInfo") +public class AdminOrderInfoController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } +} diff --git a/src/main/java/com/cool/modules/order/controller/app/AppOrderInfoController.java b/src/main/java/com/cool/modules/order/controller/app/AppOrderInfoController.java new file mode 100644 index 0000000..40348e2 --- /dev/null +++ b/src/main/java/com/cool/modules/order/controller/app/AppOrderInfoController.java @@ -0,0 +1,33 @@ +package com.cool.modules.order.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.request.R; +import com.cool.modules.order.entity.OrderInfoEntity; +import com.cool.modules.order.service.OrderInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "订单", description = "App端订单") +@CoolRestController +public class AppOrderInfoController { + + private final OrderInfoService orderInfoService; + + @Operation(summary = "订单列表") + @GetMapping("/list") + public R list(@RequestAttribute JSONObject requestParams) { + return R.ok(orderInfoService.list(requestParams, null)); + } + + @Operation(summary = "订单详情") + @GetMapping("/detail") + public R detail(@RequestAttribute JSONObject requestParams) { + Long id = requestParams.getLong("id"); + return R.ok(orderInfoService.info(id)); + } +} diff --git a/src/main/java/com/cool/modules/order/entity/OrderInfoEntity.java b/src/main/java/com/cool/modules/order/entity/OrderInfoEntity.java new file mode 100644 index 0000000..dd2821e --- /dev/null +++ b/src/main/java/com/cool/modules/order/entity/OrderInfoEntity.java @@ -0,0 +1,29 @@ +package com.cool.modules.order.entity; + +import com.cool.core.base.TenantEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import java.math.BigDecimal; + +@Getter +@Setter +@Table(value = "order_info", comment = "订单信息表") +public class OrderInfoEntity extends TenantEntity { + + @ColumnDefine(comment = "订单号", notNull = true) + private String orderNo; + + @ColumnDefine(comment = "用户ID", type = "bigint") + private Long userId; + + @ColumnDefine(comment = "订单金额") + private BigDecimal amount; + + @ColumnDefine(comment = "状态 0:待支付 1:已支付 2:已完成 3:已取消", defaultValue = "0") + private Integer status; + + @ColumnDefine(comment = "备注", type = "text") + private String remark; +} diff --git a/src/main/java/com/cool/modules/order/mapper/OrderInfoMapper.java b/src/main/java/com/cool/modules/order/mapper/OrderInfoMapper.java new file mode 100644 index 0000000..add7578 --- /dev/null +++ b/src/main/java/com/cool/modules/order/mapper/OrderInfoMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.order.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.order.entity.OrderInfoEntity; + +public interface OrderInfoMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/order/service/OrderInfoService.java b/src/main/java/com/cool/modules/order/service/OrderInfoService.java new file mode 100644 index 0000000..a9f5d0e --- /dev/null +++ b/src/main/java/com/cool/modules/order/service/OrderInfoService.java @@ -0,0 +1,7 @@ +package com.cool.modules.order.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.order.entity.OrderInfoEntity; + +public interface OrderInfoService extends BaseService { +} diff --git a/src/main/java/com/cool/modules/order/service/impl/OrderInfoServiceImpl.java b/src/main/java/com/cool/modules/order/service/impl/OrderInfoServiceImpl.java new file mode 100644 index 0000000..745628d --- /dev/null +++ b/src/main/java/com/cool/modules/order/service/impl/OrderInfoServiceImpl.java @@ -0,0 +1,12 @@ +package com.cool.modules.order.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.order.entity.OrderInfoEntity; +import com.cool.modules.order.mapper.OrderInfoMapper; +import com.cool.modules.order.service.OrderInfoService; +import org.springframework.stereotype.Service; + +@Service +public class OrderInfoServiceImpl extends BaseServiceImpl implements + OrderInfoService { +} diff --git a/src/main/java/com/cool/modules/org/controller/admin/AdminAppModuleController.java b/src/main/java/com/cool/modules/org/controller/admin/AdminAppModuleController.java new file mode 100644 index 0000000..6df9957 --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/admin/AdminAppModuleController.java @@ -0,0 +1,23 @@ +package com.cool.modules.org.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.org.entity.AppModuleEntity; +import com.cool.modules.org.service.AppModuleService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "模块市场", description = "功能模块管理") +@CoolRestController(api = {"add", "delete", "update", "page", "info"}) +public class AdminAppModuleController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } +} diff --git a/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgController.java b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgController.java new file mode 100644 index 0000000..d23a1a3 --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgController.java @@ -0,0 +1,54 @@ +package com.cool.modules.org.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.org.entity.AppOrgEntity; +import com.cool.modules.org.service.AppOrgService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@RequiredArgsConstructor +@Tag(name = "组织管理", description = "组织管理") +@CoolRestController(api = {"delete", "update", "page", "info"}, cname = "org") +public class AdminAppOrgController extends BaseController { + + private final AppOrgService appOrgService; + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } + + @Operation(summary = "创建组织") + @PostMapping("/add") + public R add(@RequestBody JSONObject requestParams) { + AppOrgEntity entity = requestParams.toBean(AppOrgEntity.class); + appOrgService.createOrg(requestParams, entity); + return R.ok(); + } + + @Operation(summary = "授权模块") + @PostMapping("/authorizeModules") + public R authorizeModules(@RequestBody JSONObject requestParams) { + Long orgId = requestParams.getLong("orgId"); + List moduleKeys = requestParams.getBeanList("moduleKeys", String.class); + appOrgService.authorizeModules(orgId, moduleKeys); + return R.ok(); + } + + @Operation(summary = "切换组织") + @PostMapping("/switch") + public R switchOrg(@RequestBody JSONObject requestParams) { + Long orgId = requestParams.getLong("orgId"); + Long userId = com.cool.core.util.CoolSecurityUtil.getCurrentUserId(); + appOrgService.switchOrg(userId, orgId, true); + return R.ok(); + } +} diff --git a/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgModuleController.java b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgModuleController.java new file mode 100644 index 0000000..c299d2f --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgModuleController.java @@ -0,0 +1,22 @@ +package com.cool.modules.org.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import com.cool.modules.org.service.AppOrgModuleService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "组织模块授权", description = "组织模块授权管理") +@CoolRestController(api = {"add", "delete", "update", "list"}, cname = "orgModule") +public class AdminAppOrgModuleController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setListOption(createOp()); + } +} diff --git a/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgUserController.java b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgUserController.java new file mode 100644 index 0000000..66bea82 --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/admin/AdminAppOrgUserController.java @@ -0,0 +1,50 @@ +package com.cool.modules.org.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.org.entity.AppOrgUserEntity; +import com.cool.modules.org.service.AppOrgUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; + +@RequiredArgsConstructor +@Tag(name = "组织成员管理", description = "组织成员管理") +@CoolRestController(api = {"page", "list", "update", "delete"}, cname = "orgUser") +public class AdminAppOrgUserController extends BaseController { + + private final AppOrgUserService appOrgUserService; + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } + + @Operation(summary = "获取组织成员列表") + @PostMapping("/memberList") + public R memberList(@RequestAttribute JSONObject requestParams) { + Long orgId = requestParams.getLong("orgId"); + List> members = appOrgUserService.getMemberListByOrgId(orgId); + return R.ok(members); + } + + @Operation(summary = "提升/降级App端管理员") + @PostMapping("/changeRole") + public R changeRole(@RequestBody JSONObject requestParams) { + Long orgUserId = requestParams.getLong("id"); + String role = requestParams.getStr("role"); + AppOrgUserEntity orgUser = appOrgUserService.getById(orgUserId); + com.cool.core.exception.CoolPreconditions.checkEmpty(orgUser, "记录不存在"); + orgUser.setRole(role); + appOrgUserService.update(orgUser); + return R.ok(); + } +} diff --git a/src/main/java/com/cool/modules/org/controller/app/AppOrgController.java b/src/main/java/com/cool/modules/org/controller/app/AppOrgController.java new file mode 100644 index 0000000..08194ba --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/app/AppOrgController.java @@ -0,0 +1,70 @@ +package com.cool.modules.org.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.request.R; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.org.service.AppOrgService; +import com.cool.modules.org.service.AppOrgUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; + +@RequiredArgsConstructor +@Tag(name = "组织", description = "App端组织管理") +@CoolRestController +public class AppOrgController { + + private final AppOrgService appOrgService; + private final AppOrgUserService appOrgUserService; + + @Operation(summary = "获取已加入的组织列表") + @GetMapping("/list") + public R list() { + Long userId = CoolSecurityUtil.getCurrentUserId(); + List> orgList = appOrgUserService.getOrgListByUserId(userId); + return R.ok(orgList); + } + + @Operation(summary = "通过邀请码加入组织") + @PostMapping("/join") + public R join(@RequestBody JSONObject requestParams) { + Long userId = CoolSecurityUtil.getCurrentUserId(); + String inviteCode = requestParams.getStr("inviteCode"); + appOrgUserService.joinOrg(userId, inviteCode); + return R.ok(); + } + + @Operation(summary = "通过orgId加入组织") + @PostMapping("/joinById") + public R joinById(@RequestBody JSONObject requestParams) { + Long userId = CoolSecurityUtil.getCurrentUserId(); + Long orgId = requestParams.getLong("orgId"); + appOrgUserService.joinOrgById(userId, orgId); + return R.ok(); + } + + @Operation(summary = "退出组织") + @PostMapping("/quit") + public R quit(@RequestBody JSONObject requestParams) { + Long userId = CoolSecurityUtil.getCurrentUserId(); + Long orgId = requestParams.getLong("orgId"); + appOrgUserService.quitOrg(userId, orgId); + return R.ok(); + } + + @Operation(summary = "切换组织") + @PostMapping("/switch") + public R switchOrg(@RequestBody JSONObject requestParams) { + Long userId = CoolSecurityUtil.getCurrentUserId(); + Long orgId = requestParams.getLong("orgId"); + Object result = appOrgService.switchOrg(userId, orgId, false); + return R.ok(result); + } +} diff --git a/src/main/java/com/cool/modules/org/controller/app/AppOrgUserController.java b/src/main/java/com/cool/modules/org/controller/app/AppOrgUserController.java new file mode 100644 index 0000000..5d22643 --- /dev/null +++ b/src/main/java/com/cool/modules/org/controller/app/AppOrgUserController.java @@ -0,0 +1,44 @@ +package com.cool.modules.org.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.request.R; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.org.service.AppOrgUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@RequiredArgsConstructor +@Tag(name = "组织成员", description = "App端组织成员管理") +@CoolRestController +public class AppOrgUserController { + + private final AppOrgUserService appOrgUserService; + + @Operation(summary = "获取组织成员列表") + @PostMapping("/memberList") + public R memberList(@RequestBody JSONObject requestParams) { + Long orgId = requestParams.getLong("orgId"); + Long userId = CoolSecurityUtil.getCurrentUserId(); + CoolPreconditions.check(!appOrgUserService.isOrgMember(userId, orgId), "无权查看该组织成员"); + List> members = appOrgUserService.getMemberListByOrgId(orgId); + return R.ok(members); + } + + @Operation(summary = "审批成员加入") + @PostMapping("/approve") + public R approve(@RequestBody JSONObject requestParams) { + Long orgId = requestParams.getLong("orgId"); + Long targetUserId = requestParams.getLong("userId"); + Boolean approved = requestParams.getBool("approved", true); + Long adminUserId = CoolSecurityUtil.getCurrentUserId(); + appOrgUserService.approve(adminUserId, orgId, targetUserId, approved); + return R.ok(); + } +} diff --git a/src/main/java/com/cool/modules/org/entity/AppModuleEntity.java b/src/main/java/com/cool/modules/org/entity/AppModuleEntity.java new file mode 100644 index 0000000..511d47b --- /dev/null +++ b/src/main/java/com/cool/modules/org/entity/AppModuleEntity.java @@ -0,0 +1,39 @@ +package com.cool.modules.org.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "app_module", comment = "功能模块注册表") +public class AppModuleEntity extends BaseEntity { + + @UniIndex + @ColumnDefine(comment = "模块标识", notNull = true) + private String moduleKey; + + @ColumnDefine(comment = "模块名称", notNull = true) + private String name; + + @ColumnDefine(comment = "模块图标") + private String icon; + + @ColumnDefine(comment = "模块描述", type = "text") + private String description; + + @ColumnDefine(comment = "关联的admin菜单ID列表", type = "text") + private String menuIds; + + @ColumnDefine(comment = "关联的App页面路由配置", type = "text") + private String appPages; + + @ColumnDefine(comment = "要求的最低App版本号") + private String minAppVersion; + + @ColumnDefine(comment = "状态 0:禁用 1:启用", defaultValue = "1") + private Integer status; +} diff --git a/src/main/java/com/cool/modules/org/entity/AppOrgEntity.java b/src/main/java/com/cool/modules/org/entity/AppOrgEntity.java new file mode 100644 index 0000000..e0e91d1 --- /dev/null +++ b/src/main/java/com/cool/modules/org/entity/AppOrgEntity.java @@ -0,0 +1,42 @@ +package com.cool.modules.org.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "app_org", comment = "组织表") +public class AppOrgEntity extends BaseEntity { + + @ColumnDefine(comment = "组织名称", notNull = true) + private String name; + + @ColumnDefine(comment = "组织类型") + private String type; + + @ColumnDefine(comment = "组织图标/Logo") + private String icon; + + @ColumnDefine(comment = "组织简介", type = "text") + private String description; + + @UniIndex + @ColumnDefine(comment = "邀请码") + private String inviteCode; + + @ColumnDefine(comment = "加入方式 0:自由加入 1:需审批", defaultValue = "0") + private Integer joinType; + + @ColumnDefine(comment = "状态 0:禁用 1:启用", defaultValue = "1") + private Integer status; + + @ColumnDefine(comment = "Admin端组织管理员userId", type = "bigint") + private Long adminUserId; + + @ColumnDefine(comment = "组织级扩展配置", type = "text") + private String config; +} diff --git a/src/main/java/com/cool/modules/org/entity/AppOrgModuleEntity.java b/src/main/java/com/cool/modules/org/entity/AppOrgModuleEntity.java new file mode 100644 index 0000000..ce89f25 --- /dev/null +++ b/src/main/java/com/cool/modules/org/entity/AppOrgModuleEntity.java @@ -0,0 +1,29 @@ +package com.cool.modules.org.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "app_org_module", comment = "组织模块关联表") +public class AppOrgModuleEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "组织ID", type = "bigint", notNull = true) + private Long orgId; + + @Index + @ColumnDefine(comment = "模块标识", notNull = true) + private String moduleKey; + + @ColumnDefine(comment = "组织级模块配置", type = "text") + private String config; + + @ColumnDefine(comment = "状态 0:禁用 1:启用", defaultValue = "1") + private Integer status; +} diff --git a/src/main/java/com/cool/modules/org/entity/AppOrgUserEntity.java b/src/main/java/com/cool/modules/org/entity/AppOrgUserEntity.java new file mode 100644 index 0000000..e2a4b05 --- /dev/null +++ b/src/main/java/com/cool/modules/org/entity/AppOrgUserEntity.java @@ -0,0 +1,44 @@ +package com.cool.modules.org.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +import java.util.Date; + +@Getter +@Setter +@Table(value = "app_org_user", comment = "组织用户关联表") +public class AppOrgUserEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "组织ID", type = "bigint", notNull = true) + private Long orgId; + + @Index + @ColumnDefine(comment = "App用户ID", type = "bigint", notNull = true) + private Long userId; + + @ColumnDefine(comment = "角色 member/admin", defaultValue = "member") + private String role; + + @ColumnDefine(comment = "状态 0:待审批 1:已加入 2:已拒绝 3:已退出", defaultValue = "0") + private Integer status; + + @ColumnDefine(comment = "加入时间") + private Date joinTime; + + @Column(ignore = true) + private String orgName; + + @Column(ignore = true) + private String nickName; + + @Column(ignore = true) + private String avatarUrl; +} diff --git a/src/main/java/com/cool/modules/org/mapper/AppModuleMapper.java b/src/main/java/com/cool/modules/org/mapper/AppModuleMapper.java new file mode 100644 index 0000000..7c9eacb --- /dev/null +++ b/src/main/java/com/cool/modules/org/mapper/AppModuleMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.org.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.org.entity.AppModuleEntity; + +public interface AppModuleMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/org/mapper/AppOrgMapper.java b/src/main/java/com/cool/modules/org/mapper/AppOrgMapper.java new file mode 100644 index 0000000..2067368 --- /dev/null +++ b/src/main/java/com/cool/modules/org/mapper/AppOrgMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.org.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.org.entity.AppOrgEntity; + +public interface AppOrgMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/org/mapper/AppOrgModuleMapper.java b/src/main/java/com/cool/modules/org/mapper/AppOrgModuleMapper.java new file mode 100644 index 0000000..047ce34 --- /dev/null +++ b/src/main/java/com/cool/modules/org/mapper/AppOrgModuleMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.org.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.org.entity.AppOrgModuleEntity; + +public interface AppOrgModuleMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/org/mapper/AppOrgUserMapper.java b/src/main/java/com/cool/modules/org/mapper/AppOrgUserMapper.java new file mode 100644 index 0000000..e5c6e7a --- /dev/null +++ b/src/main/java/com/cool/modules/org/mapper/AppOrgUserMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.org.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.org.entity.AppOrgUserEntity; + +public interface AppOrgUserMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/org/service/AppModuleService.java b/src/main/java/com/cool/modules/org/service/AppModuleService.java new file mode 100644 index 0000000..c49b1d3 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/AppModuleService.java @@ -0,0 +1,7 @@ +package com.cool.modules.org.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.org.entity.AppModuleEntity; + +public interface AppModuleService extends BaseService { +} diff --git a/src/main/java/com/cool/modules/org/service/AppOrgModuleService.java b/src/main/java/com/cool/modules/org/service/AppOrgModuleService.java new file mode 100644 index 0000000..6488df2 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/AppOrgModuleService.java @@ -0,0 +1,12 @@ +package com.cool.modules.org.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import java.util.List; + +public interface AppOrgModuleService extends BaseService { + + List getModuleKeysByOrgId(Long orgId); + + boolean isModuleAuthorized(Long orgId, String moduleKey); +} diff --git a/src/main/java/com/cool/modules/org/service/AppOrgService.java b/src/main/java/com/cool/modules/org/service/AppOrgService.java new file mode 100644 index 0000000..e970631 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/AppOrgService.java @@ -0,0 +1,16 @@ +package com.cool.modules.org.service; + +import cn.hutool.core.lang.Dict; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseService; +import com.cool.modules.org.entity.AppOrgEntity; +import java.util.List; + +public interface AppOrgService extends BaseService { + + void createOrg(JSONObject requestParams, AppOrgEntity entity); + + void authorizeModules(Long orgId, List moduleKeys); + + Dict switchOrg(Long userId, Long orgId, boolean isAdmin); +} diff --git a/src/main/java/com/cool/modules/org/service/AppOrgUserService.java b/src/main/java/com/cool/modules/org/service/AppOrgUserService.java new file mode 100644 index 0000000..809c861 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/AppOrgUserService.java @@ -0,0 +1,25 @@ +package com.cool.modules.org.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.org.entity.AppOrgUserEntity; +import java.util.List; +import java.util.Map; + +public interface AppOrgUserService extends BaseService { + + void joinOrg(Long userId, String inviteCode); + + void joinOrgById(Long userId, Long orgId); + + void quitOrg(Long userId, Long orgId); + + void approve(Long adminUserId, Long orgId, Long targetUserId, boolean approved); + + List> getOrgListByUserId(Long userId); + + List> getMemberListByOrgId(Long orgId); + + boolean isOrgAdmin(Long userId, Long orgId); + + boolean isOrgMember(Long userId, Long orgId); +} diff --git a/src/main/java/com/cool/modules/org/service/impl/AppModuleServiceImpl.java b/src/main/java/com/cool/modules/org/service/impl/AppModuleServiceImpl.java new file mode 100644 index 0000000..5dbd46e --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/impl/AppModuleServiceImpl.java @@ -0,0 +1,12 @@ +package com.cool.modules.org.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.org.mapper.AppModuleMapper; +import com.cool.modules.org.entity.AppModuleEntity; +import com.cool.modules.org.service.AppModuleService; +import org.springframework.stereotype.Service; + +@Service +public class AppModuleServiceImpl extends BaseServiceImpl implements + AppModuleService { +} diff --git a/src/main/java/com/cool/modules/org/service/impl/AppOrgModuleServiceImpl.java b/src/main/java/com/cool/modules/org/service/impl/AppOrgModuleServiceImpl.java new file mode 100644 index 0000000..72cbcef --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/impl/AppOrgModuleServiceImpl.java @@ -0,0 +1,35 @@ +package com.cool.modules.org.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import com.cool.modules.org.mapper.AppOrgModuleMapper; +import com.cool.modules.org.service.AppOrgModuleService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; + +@Service +public class AppOrgModuleServiceImpl extends BaseServiceImpl implements + AppOrgModuleService { + + @Override + public List getModuleKeysByOrgId(Long orgId) { + List modules = list(QueryWrapper.create() + .eq(AppOrgModuleEntity::getOrgId, orgId) + .eq(AppOrgModuleEntity::getStatus, 1)); + return modules.stream() + .map(AppOrgModuleEntity::getModuleKey) + .collect(Collectors.toList()); + } + + @Override + public boolean isModuleAuthorized(Long orgId, String moduleKey) { + AppOrgModuleEntity module = getOne(QueryWrapper.create() + .eq(AppOrgModuleEntity::getOrgId, orgId) + .eq(AppOrgModuleEntity::getModuleKey, moduleKey) + .eq(AppOrgModuleEntity::getStatus, 1)); + return module != null; + } +} diff --git a/src/main/java/com/cool/modules/org/service/impl/AppOrgServiceImpl.java b/src/main/java/com/cool/modules/org/service/impl/AppOrgServiceImpl.java new file mode 100644 index 0000000..9f43e25 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/impl/AppOrgServiceImpl.java @@ -0,0 +1,186 @@ +package com.cool.modules.org.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.security.jwt.JwtTokenUtil; +import com.cool.core.security.jwt.JwtUser; +import com.cool.modules.base.entity.sys.BaseSysRoleEntity; +import com.cool.modules.base.entity.sys.BaseSysUserRoleEntity; +import com.cool.modules.base.mapper.sys.BaseSysRoleMapper; +import com.cool.modules.base.mapper.sys.BaseSysUserMapper; +import com.cool.modules.base.mapper.sys.BaseSysUserRoleMapper; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.cool.modules.org.entity.AppModuleEntity; +import com.cool.modules.org.entity.AppOrgEntity; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import com.cool.modules.org.mapper.AppOrgMapper; +import com.cool.modules.org.mapper.AppModuleMapper; +import com.cool.modules.org.service.AppOrgModuleService; +import com.cool.modules.org.service.AppOrgService; +import com.cool.modules.org.service.AppOrgUserService; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.tenant.TenantManager; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class AppOrgServiceImpl extends BaseServiceImpl implements + AppOrgService { + + private final AppModuleMapper appModuleMapper; + private final BaseSysRoleMapper baseSysRoleMapper; + private final BaseSysUserRoleMapper baseSysUserRoleMapper; + private final BaseSysUserMapper baseSysUserMapper; + private final BaseSysPermsService baseSysPermsService; + private final AppOrgModuleService appOrgModuleService; + private final AppOrgUserService appOrgUserService; + private final CoolCache coolCache; + private final JwtTokenUtil jwtTokenUtil; + + @Override + @Transactional + public void createOrg(JSONObject requestParams, AppOrgEntity entity) { + entity.setInviteCode(generateInviteCode()); + this.add(entity); + + Long orgId = entity.getId(); + + BaseSysRoleEntity role = new BaseSysRoleEntity(); + role.setName(entity.getName() + "管理员"); + role.setLabel("org_admin_" + orgId); + role.setRemark("组织[" + entity.getName() + "]自动创建的管理角色"); + role.setStatus(1); + role.setTenantId(orgId); + TenantManager.withoutTenantCondition(() -> { + baseSysRoleMapper.insertSelective(role); + }); + + if (entity.getAdminUserId() != null) { + BaseSysUserRoleEntity userRole = new BaseSysUserRoleEntity(); + userRole.setUserId(entity.getAdminUserId()); + userRole.setRoleId(role.getId()); + TenantManager.withoutTenantCondition(() -> { + baseSysUserRoleMapper.insertSelective(userRole); + }); + + com.cool.modules.base.entity.sys.BaseSysUserEntity adminUser = baseSysUserMapper.selectOneById(entity.getAdminUserId()); + if (adminUser != null) { + adminUser.setTenantId(orgId); + TenantManager.withoutTenantCondition(() -> { + baseSysUserMapper.update(adminUser); + }); + } + } + + if (requestParams != null) { + List moduleKeys = requestParams.getBeanList("moduleKeys", String.class); + if (moduleKeys != null && !moduleKeys.isEmpty()) { + authorizeModules(orgId, moduleKeys); + } + } + } + + @Override + @Transactional + public void authorizeModules(Long orgId, List moduleKeys) { + QueryWrapper qw = QueryWrapper.create() + .eq(AppOrgModuleEntity::getOrgId, orgId); + appOrgModuleService.remove(qw); + + for (String moduleKey : moduleKeys) { + AppModuleEntity module = appModuleMapper.selectOneByQuery( + QueryWrapper.create().eq(AppModuleEntity::getModuleKey, moduleKey) + ); + CoolPreconditions.checkEmpty(module, "模块{}不存在", moduleKey); + + AppOrgModuleEntity orgModule = new AppOrgModuleEntity(); + orgModule.setOrgId(orgId); + orgModule.setModuleKey(moduleKey); + orgModule.setStatus(1); + appOrgModuleService.add(orgModule); + } + + BaseSysRoleEntity role = TenantManager.withoutTenantCondition(() -> + baseSysRoleMapper.selectOneByQuery( + QueryWrapper.create().eq(BaseSysRoleEntity::getLabel, "org_admin_" + orgId) + ) + ); + if (role != null) { + List menuIds = new java.util.ArrayList<>(); + for (String moduleKey : moduleKeys) { + AppModuleEntity module = appModuleMapper.selectOneByQuery( + QueryWrapper.create().eq(AppModuleEntity::getModuleKey, moduleKey) + ); + if (module != null && StrUtil.isNotBlank(module.getMenuIds())) { + cn.hutool.json.JSONArray arr = new cn.hutool.json.JSONArray(module.getMenuIds()); + for (Object o : arr) { + menuIds.add(Long.valueOf(o.toString())); + } + } + } + baseSysPermsService.updatePerms(role.getId(), menuIds.toArray(new Long[0]), null); + } + } + + @Override + public Dict switchOrg(Long userId, Long orgId, boolean isAdmin) { + if (isAdmin) { + BaseSysUserRoleEntity userRole = baseSysUserRoleMapper.selectOneByQuery( + QueryWrapper.create().eq(BaseSysUserRoleEntity::getUserId, userId) + ); + CoolPreconditions.checkEmpty(userRole, "无权切换到该组织"); + + BaseSysRoleEntity role = baseSysRoleMapper.selectOneById(userRole.getRoleId()); + CoolPreconditions.checkEmpty(role, "角色不存在"); + CoolPreconditions.check(!orgId.equals(role.getTenantId()), "无权切换到该组织"); + } else { + CoolPreconditions.check(!appOrgUserService.isOrgMember(userId, orgId), "未加入该组织"); + } + + AppOrgEntity org = this.getById(orgId); + CoolPreconditions.checkEmpty(org, "组织不存在"); + CoolPreconditions.check(org.getStatus() == 0, "组织已停用"); + + if (!isAdmin) { + Dict tokenInfo = Dict.create() + .set("userType", UserTypeEnum.APP.name()) + .set("userId", userId) + .set("orgId", orgId); + String token = jwtTokenUtil.generateToken(tokenInfo); + String refreshToken = jwtTokenUtil.generateRefreshToken(tokenInfo); + + JwtUser jwtUser = new JwtUser(userId, + List.of(new SimpleGrantedAuthority("ROLE_" + UserTypeEnum.APP.name())), + true); + coolCache.set("app:userDetails:" + userId, jwtUser); + + return Dict.create() + .set("token", token) + .set("expire", jwtTokenUtil.getExpire()) + .set("refreshToken", refreshToken) + .set("refreshExpire", jwtTokenUtil.getRefreshExpire()) + .set("orgId", orgId); + } + return Dict.create().set("orgId", orgId); + } + + private String generateInviteCode() { + String code; + do { + code = RandomUtil.randomStringUpper(6); + } while (this.getOne( + QueryWrapper.create().eq(AppOrgEntity::getInviteCode, code) + ) != null); + return code; + } +} diff --git a/src/main/java/com/cool/modules/org/service/impl/AppOrgUserServiceImpl.java b/src/main/java/com/cool/modules/org/service/impl/AppOrgUserServiceImpl.java new file mode 100644 index 0000000..8225559 --- /dev/null +++ b/src/main/java/com/cool/modules/org/service/impl/AppOrgUserServiceImpl.java @@ -0,0 +1,180 @@ +package com.cool.modules.org.service.impl; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.exception.CoolPreconditions; +import com.cool.modules.org.entity.AppOrgEntity; +import com.cool.modules.org.entity.AppOrgModuleEntity; +import com.cool.modules.org.entity.AppOrgUserEntity; +import com.cool.modules.org.mapper.AppOrgMapper; +import com.cool.modules.org.mapper.AppOrgUserMapper; +import com.cool.modules.org.service.AppOrgModuleService; +import com.cool.modules.org.service.AppOrgUserService; +import com.cool.modules.user.entity.UserInfoEntity; +import com.cool.modules.user.mapper.UserInfoMapper; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AppOrgUserServiceImpl extends BaseServiceImpl implements + AppOrgUserService { + + private final AppOrgModuleService appOrgModuleService; + private final UserInfoMapper userInfoMapper; + private final AppOrgMapper appOrgMapper; + + @Override + public void joinOrg(Long userId, String inviteCode) { + CoolPreconditions.check(StrUtil.isBlank(inviteCode), "邀请码不能为空"); + + AppOrgEntity orgEntity = appOrgMapper.selectOneByQuery( + QueryWrapper.create().eq(AppOrgEntity::getInviteCode, inviteCode) + ); + CoolPreconditions.checkEmpty(orgEntity, "邀请码无效"); + CoolPreconditions.check(orgEntity.getStatus() == 0, "组织已停用"); + + joinOrgById(userId, orgEntity.getId()); + } + + @Override + public void joinOrgById(Long userId, Long orgId) { + AppOrgUserEntity existing = getOne(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getUserId, userId)); + CoolPreconditions.check(existing != null && existing.getStatus() == 1, "已加入该组织"); + CoolPreconditions.check(existing != null && existing.getStatus() == 0, "已申请加入,等待审批"); + + AppOrgEntity orgEntity = appOrgMapper.selectOneById(orgId); + CoolPreconditions.checkEmpty(orgEntity, "组织不存在"); + CoolPreconditions.check(orgEntity.getStatus() == 0, "组织已停用"); + + AppOrgUserEntity orgUser = new AppOrgUserEntity(); + orgUser.setOrgId(orgId); + orgUser.setUserId(userId); + orgUser.setRole("member"); + + if (orgEntity.getJoinType() == 0) { + orgUser.setStatus(1); + orgUser.setJoinTime(new java.util.Date()); + } else { + orgUser.setStatus(0); + } + + if (existing != null) { + orgUser.setId(existing.getId()); + this.update(orgUser); + } else { + this.add(orgUser); + } + } + + @Override + public void quitOrg(Long userId, Long orgId) { + AppOrgUserEntity orgUser = getOne(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getUserId, userId) + .eq(AppOrgUserEntity::getStatus, 1)); + CoolPreconditions.checkEmpty(orgUser, "未加入该组织"); + + orgUser.setStatus(3); + this.update(orgUser); + } + + @Override + public void approve(Long adminUserId, Long orgId, Long targetUserId, boolean approved) { + CoolPreconditions.check(!isOrgAdmin(adminUserId, orgId), "无审批权限"); + + AppOrgUserEntity orgUser = getOne(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getUserId, targetUserId) + .eq(AppOrgUserEntity::getStatus, 0)); + CoolPreconditions.checkEmpty(orgUser, "无待审批记录"); + + orgUser.setStatus(approved ? 1 : 2); + if (approved) { + orgUser.setJoinTime(new java.util.Date()); + } + this.update(orgUser); + } + + @Override + public List> getOrgListByUserId(Long userId) { + List orgUsers = list(QueryWrapper.create() + .eq(AppOrgUserEntity::getUserId, userId) + .eq(AppOrgUserEntity::getStatus, 1)); + + List> result = new ArrayList<>(); + for (AppOrgUserEntity ou : orgUsers) { + Map item = new HashMap<>(); + item.put("orgId", ou.getOrgId()); + item.put("role", ou.getRole()); + item.put("joinTime", ou.getJoinTime()); + + AppOrgEntity org = appOrgMapper.selectOneById(ou.getOrgId()); + if (org != null) { + item.put("name", org.getName()); + item.put("icon", org.getIcon()); + item.put("type", org.getType()); + item.put("status", org.getStatus()); + } + + // Get modules + List moduleKeys = appOrgModuleService.getModuleKeysByOrgId(ou.getOrgId()); + item.put("modules", moduleKeys); + + result.add(item); + } + return result; + } + + @Override + public List> getMemberListByOrgId(Long orgId) { + List orgUsers = list(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getStatus, 1)); + + List> result = new ArrayList<>(); + for (AppOrgUserEntity ou : orgUsers) { + Map item = new HashMap<>(); + item.put("id", ou.getId()); + item.put("userId", ou.getUserId()); + item.put("role", ou.getRole()); + item.put("joinTime", ou.getJoinTime()); + + UserInfoEntity user = userInfoMapper.selectOneById(ou.getUserId()); + if (user != null) { + item.put("nickName", user.getNickName()); + item.put("avatarUrl", user.getAvatarUrl()); + item.put("phone", user.getPhone()); + } + result.add(item); + } + return result; + } + + @Override + public boolean isOrgAdmin(Long userId, Long orgId) { + AppOrgUserEntity orgUser = getOne(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getUserId, userId) + .eq(AppOrgUserEntity::getStatus, 1) + .eq(AppOrgUserEntity::getRole, "admin")); + return orgUser != null; + } + + @Override + public boolean isOrgMember(Long userId, Long orgId) { + AppOrgUserEntity orgUser = getOne(QueryWrapper.create() + .eq(AppOrgUserEntity::getOrgId, orgId) + .eq(AppOrgUserEntity::getUserId, userId) + .eq(AppOrgUserEntity::getStatus, 1)); + return orgUser != null; + } +} diff --git a/src/main/java/com/cool/modules/zoo/controller/admin/AdminZooAnimalController.java b/src/main/java/com/cool/modules/zoo/controller/admin/AdminZooAnimalController.java new file mode 100644 index 0000000..17e901c --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/controller/admin/AdminZooAnimalController.java @@ -0,0 +1,22 @@ +package com.cool.modules.zoo.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.zoo.entity.ZooAnimalEntity; +import com.cool.modules.zoo.service.ZooAnimalService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "动物管理", description = "动物信息管理") +@CoolRestController(api = {"add", "delete", "update", "page", "info"}) +public class AdminZooAnimalController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp()); + } +} diff --git a/src/main/java/com/cool/modules/zoo/controller/app/AppZooAnimalController.java b/src/main/java/com/cool/modules/zoo/controller/app/AppZooAnimalController.java new file mode 100644 index 0000000..6607cda --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/controller/app/AppZooAnimalController.java @@ -0,0 +1,33 @@ +package com.cool.modules.zoo.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.request.R; +import com.cool.modules.zoo.entity.ZooAnimalEntity; +import com.cool.modules.zoo.service.ZooAnimalService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "动物", description = "App端动物") +@CoolRestController +public class AppZooAnimalController { + + private final ZooAnimalService zooAnimalService; + + @Operation(summary = "动物列表") + @GetMapping("/list") + public R list(@RequestAttribute JSONObject requestParams) { + return R.ok(zooAnimalService.list(requestParams, null)); + } + + @Operation(summary = "动物详情") + @GetMapping("/detail") + public R detail(@RequestAttribute JSONObject requestParams) { + Long id = requestParams.getLong("id"); + return R.ok(zooAnimalService.info(id)); + } +} diff --git a/src/main/java/com/cool/modules/zoo/entity/ZooAnimalEntity.java b/src/main/java/com/cool/modules/zoo/entity/ZooAnimalEntity.java new file mode 100644 index 0000000..35f6658 --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/entity/ZooAnimalEntity.java @@ -0,0 +1,31 @@ +package com.cool.modules.zoo.entity; + +import com.cool.core.base.TenantEntity; +import com.mybatisflex.annotation.Table; +import com.cool.core.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "zoo_animal", comment = "动物信息表") +public class ZooAnimalEntity extends TenantEntity { + + @ColumnDefine(comment = "动物名称", notNull = true) + private String name; + + @ColumnDefine(comment = "分类") + private String category; + + @ColumnDefine(comment = "展区") + private String area; + + @ColumnDefine(comment = "图片") + private String image; + + @ColumnDefine(comment = "描述", type = "text") + private String description; + + @ColumnDefine(comment = "状态 0:离开 1:展出", defaultValue = "1") + private Integer status; +} diff --git a/src/main/java/com/cool/modules/zoo/mapper/ZooAnimalMapper.java b/src/main/java/com/cool/modules/zoo/mapper/ZooAnimalMapper.java new file mode 100644 index 0000000..2bf04ca --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/mapper/ZooAnimalMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.zoo.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.zoo.entity.ZooAnimalEntity; + +public interface ZooAnimalMapper extends BaseMapper { +} diff --git a/src/main/java/com/cool/modules/zoo/service/ZooAnimalService.java b/src/main/java/com/cool/modules/zoo/service/ZooAnimalService.java new file mode 100644 index 0000000..ee65479 --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/service/ZooAnimalService.java @@ -0,0 +1,7 @@ +package com.cool.modules.zoo.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.zoo.entity.ZooAnimalEntity; + +public interface ZooAnimalService extends BaseService { +} diff --git a/src/main/java/com/cool/modules/zoo/service/impl/ZooAnimalServiceImpl.java b/src/main/java/com/cool/modules/zoo/service/impl/ZooAnimalServiceImpl.java new file mode 100644 index 0000000..137573d --- /dev/null +++ b/src/main/java/com/cool/modules/zoo/service/impl/ZooAnimalServiceImpl.java @@ -0,0 +1,12 @@ +package com.cool.modules.zoo.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.zoo.entity.ZooAnimalEntity; +import com.cool.modules.zoo.mapper.ZooAnimalMapper; +import com.cool.modules.zoo.service.ZooAnimalService; +import org.springframework.stereotype.Service; + +@Service +public class ZooAnimalServiceImpl extends BaseServiceImpl implements + ZooAnimalService { +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f382d97..9a16fe1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -153,7 +153,7 @@ cool: queue-capacity-multiplier: 3 multi-tenant: # 是否开启多租户,默认关闭 - enable: false + enable: true # AutoTable配置,根据实体类自动生成表 auto-table: show-banner: false