From 7dede6f36fc0fee85efe85dd96b9c3ce2ad59f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sat, 28 Mar 2026 21:47:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20=E5=AE=8C=E5=96=84=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=87=AD=E8=AF=81=E3=80=81=E4=BA=8B=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增应用密钥凭证的创建、重置和状态管理功能 - 实现AppSecret自动生成功能并添加脱敏显示机制 - 增加应用操作动态的最新记录查询和批量清理功能 - 添加应用成员邀请和角色修改功能 - 优化查询条件支持精确匹配和租户隔离 - 集成网站信息关联查询并完善数据脱敏处理 --- .../controller/AppCredentialController.java | 56 ++++++++---- .../app/controller/AppEventController.java | 76 ++++++++-------- .../app/controller/AppUserController.java | 65 ++++++++++---- .../app/controller/AppVersionController.java | 88 ++++++++++++++----- .../app/mapper/xml/AppCredentialMapper.xml | 35 +++----- .../app/mapper/xml/AppEventMapper.xml | 42 ++++----- .../app/mapper/xml/AppUserMapper.xml | 36 ++++---- .../app/mapper/xml/AppVersionMapper.xml | 49 ++++------- .../app/param/AppCredentialParam.java | 14 +-- .../gxwebsoft/app/param/AppEventParam.java | 25 +++--- .../com/gxwebsoft/app/param/AppUserParam.java | 19 ++-- .../gxwebsoft/app/param/AppVersionParam.java | 33 ++----- .../app/service/AppCredentialService.java | 33 +++++-- .../app/service/AppEventService.java | 36 ++++++-- .../gxwebsoft/app/service/AppUserService.java | 36 ++++++-- .../app/service/AppVersionService.java | 32 +++++-- .../impl/AppCredentialServiceImpl.java | 86 +++++++++++++++++- .../app/service/impl/AppEventServiceImpl.java | 47 +++++++++- .../app/service/impl/AppUserServiceImpl.java | 50 ++++++++++- .../service/impl/AppVersionServiceImpl.java | 73 ++++++++++++++- 20 files changed, 634 insertions(+), 297 deletions(-) diff --git a/src/main/java/com/gxwebsoft/app/controller/AppCredentialController.java b/src/main/java/com/gxwebsoft/app/controller/AppCredentialController.java index 17e68da..6b9ce15 100644 --- a/src/main/java/com/gxwebsoft/app/controller/AppCredentialController.java +++ b/src/main/java/com/gxwebsoft/app/controller/AppCredentialController.java @@ -6,12 +6,12 @@ import com.gxwebsoft.app.entity.AppCredential; import com.gxwebsoft.app.param.AppCredentialParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.system.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -24,64 +24,86 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Tag(name = "应用密钥凭证管理") @RestController @RequestMapping("/api/app/app-credential") public class AppCredentialController extends BaseController { + @Resource private AppCredentialService appCredentialService; - @PreAuthorize("hasAuthority('app:appCredential:list')") @Operation(summary = "分页查询应用密钥凭证") @GetMapping("/page") public ApiResult> page(AppCredentialParam param) { - // 使用关联查询 return success(appCredentialService.pageRel(param)); } - @PreAuthorize("hasAuthority('app:appCredential:list')") @Operation(summary = "查询全部应用密钥凭证") @GetMapping() public ApiResult> list(AppCredentialParam param) { - // 使用关联查询 return success(appCredentialService.listRel(param)); } - @PreAuthorize("hasAuthority('app:appCredential:list')") @Operation(summary = "根据id查询应用密钥凭证") @GetMapping("/{id}") public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 return success(appCredentialService.getByIdRel(id)); } @PreAuthorize("hasAuthority('app:appCredential:save')") @OperationLog - @Operation(summary = "添加应用密钥凭证") + @Operation(summary = "创建应用密钥凭证(自动生成 AppID 和 AppSecret)") @PostMapping() public ApiResult save(@RequestBody AppCredential appCredential) { - // 记录当前登录用户id User loginUser = getLoginUser(); - if (loginUser != null) { - appCredential.setUserId(loginUser.getUserId()); + if (loginUser == null) { + return fail("请先登录"); } - if (appCredentialService.save(appCredential)) { - return success("添加成功"); - } - return fail("添加失败"); + appCredential.setUserId(loginUser.getUserId()); + // 创建并生成密钥 + AppCredential result = appCredentialService.createCredential(appCredential); + return success("创建成功,请保存 AppSecret,该信息仅展示一次", result); } @PreAuthorize("hasAuthority('app:appCredential:update')") @OperationLog - @Operation(summary = "修改应用密钥凭证") + @Operation(summary = "修改应用密钥凭证(名称/类型/备注等,不含密钥)") @PutMapping() public ApiResult update(@RequestBody AppCredential appCredential) { + // 防止通过此接口直接修改 appId/appSecret + appCredential.setAppId(null); + appCredential.setAppSecret(null); if (appCredentialService.updateById(appCredential)) { return success("修改成功"); } return fail("修改失败"); } + @PreAuthorize("hasAuthority('app:appCredential:update')") + @OperationLog + @Operation(summary = "重置 AppSecret(重新生成密钥)") + @PostMapping("/resetSecret/{id}") + public ApiResult resetSecret(@PathVariable("id") Long id) { + try { + AppCredential result = appCredentialService.resetSecret(id); + return success("重置成功,请保存新 AppSecret,该信息仅展示一次", result); + } catch (RuntimeException e) { + return fail(e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('app:appCredential:update')") + @OperationLog + @Operation(summary = "禁用/启用凭证") + @PutMapping("/status/{id}/{status}") + public ApiResult updateStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status) { + if (appCredentialService.updateStatus(id, status)) { + return success(status == 0 ? "已启用" : "已禁用"); + } + return fail("操作失败"); + } + @PreAuthorize("hasAuthority('app:appCredential:remove')") @OperationLog @Operation(summary = "删除应用密钥凭证") @@ -108,7 +130,7 @@ public class AppCredentialController extends BaseController { @OperationLog @Operation(summary = "批量修改应用密钥凭证") @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + public ApiResult updateBatch(@RequestBody BatchParam batchParam) { if (batchParam.update(appCredentialService, "id")) { return success("修改成功"); } diff --git a/src/main/java/com/gxwebsoft/app/controller/AppEventController.java b/src/main/java/com/gxwebsoft/app/controller/AppEventController.java index 84a7413..e963137 100644 --- a/src/main/java/com/gxwebsoft/app/controller/AppEventController.java +++ b/src/main/java/com/gxwebsoft/app/controller/AppEventController.java @@ -6,12 +6,11 @@ import com.gxwebsoft.app.entity.AppEvent; import com.gxwebsoft.app.param.AppEventParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.system.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -24,56 +23,64 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Tag(name = "应用操作动态管理") @RestController @RequestMapping("/api/app/app-event") public class AppEventController extends BaseController { + @Resource private AppEventService appEventService; - @PreAuthorize("hasAuthority('app:appEvent:list')") - @Operation(summary = "分页查询应用操作动态") + @Operation(summary = "分页查询操作动态") @GetMapping("/page") public ApiResult> page(AppEventParam param) { - // 使用关联查询 return success(appEventService.pageRel(param)); } - @PreAuthorize("hasAuthority('app:appEvent:list')") - @Operation(summary = "查询全部应用操作动态") + @Operation(summary = "查询全部操作动态") @GetMapping() public ApiResult> list(AppEventParam param) { - // 使用关联查询 return success(appEventService.listRel(param)); } - @PreAuthorize("hasAuthority('app:appEvent:list')") - @Operation(summary = "根据id查询应用操作动态") + @Operation(summary = "根据id查询操作动态") @GetMapping("/{id}") public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 return success(appEventService.getByIdRel(id)); } + @Operation(summary = "获取应用最新一条动态(用于卡片展示)") + @GetMapping("/latest/{websiteId}") + public ApiResult getLatest(@PathVariable("websiteId") Long websiteId) { + return success(appEventService.getLatestEvent(websiteId)); + } + @PreAuthorize("hasAuthority('app:appEvent:save')") @OperationLog - @Operation(summary = "添加应用操作动态") + @Operation(summary = "手动记录操作动态") @PostMapping() public ApiResult save(@RequestBody AppEvent appEvent) { - // 记录当前登录用户id User loginUser = getLoginUser(); if (loginUser != null) { - appEvent.setUserId(loginUser.getUserId()); + appEvent.setUserId(loginUser.getUserId()); + appEvent.setTenantId(loginUser.getTenantId()); + if (appEvent.getOperatorId() == null) { + appEvent.setOperatorId(loginUser.getUserId().longValue()); + } + if (appEvent.getOperator() == null) { + appEvent.setOperator(loginUser.getNickname()); + } } if (appEventService.save(appEvent)) { - return success("添加成功"); + return success("记录成功"); } - return fail("添加失败"); + return fail("记录失败"); } @PreAuthorize("hasAuthority('app:appEvent:update')") @OperationLog - @Operation(summary = "修改应用操作动态") + @Operation(summary = "修改操作动态") @PutMapping() public ApiResult update(@RequestBody AppEvent appEvent) { if (appEventService.updateById(appEvent)) { @@ -84,7 +91,7 @@ public class AppEventController extends BaseController { @PreAuthorize("hasAuthority('app:appEvent:remove')") @OperationLog - @Operation(summary = "删除应用操作动态") + @Operation(summary = "删除操作动态") @DeleteMapping("/{id}") public ApiResult remove(@PathVariable("id") Integer id) { if (appEventService.removeById(id)) { @@ -93,31 +100,28 @@ public class AppEventController extends BaseController { return fail("删除失败"); } - @PreAuthorize("hasAuthority('app:appEvent:save')") + @PreAuthorize("hasAuthority('app:appEvent:remove')") @OperationLog - @Operation(summary = "批量添加应用操作动态") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (appEventService.saveBatch(list)) { - return success("添加成功"); + @Operation(summary = "清空应用所有动态记录") + @DeleteMapping("/clear/{websiteId}") + public ApiResult clearByWebsiteId(@PathVariable("websiteId") Long websiteId) { + // 只清空当前租户下的数据 + AppEventParam param = new AppEventParam(); + param.setWebsiteId(websiteId); + List list = appEventService.listRel(param); + if (list.isEmpty()) { + return success("暂无动态记录"); } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('app:appEvent:update')") - @OperationLog - @Operation(summary = "批量修改应用操作动态") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(appEventService, "id")) { - return success("修改成功"); + List ids = list.stream().map(AppEvent::getId).collect(java.util.stream.Collectors.toList()); + if (appEventService.removeByIds(ids)) { + return success("已清空 " + ids.size() + " 条动态记录"); } - return fail("修改失败"); + return fail("清空失败"); } @PreAuthorize("hasAuthority('app:appEvent:remove')") @OperationLog - @Operation(summary = "批量删除应用操作动态") + @Operation(summary = "批量删除操作动态") @DeleteMapping("/batch") public ApiResult removeBatch(@RequestBody List ids) { if (appEventService.removeByIds(ids)) { diff --git a/src/main/java/com/gxwebsoft/app/controller/AppUserController.java b/src/main/java/com/gxwebsoft/app/controller/AppUserController.java index 3a7f660..5500cbe 100644 --- a/src/main/java/com/gxwebsoft/app/controller/AppUserController.java +++ b/src/main/java/com/gxwebsoft/app/controller/AppUserController.java @@ -6,12 +6,12 @@ import com.gxwebsoft.app.entity.AppUser; import com.gxwebsoft.app.param.AppUserParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.system.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -24,46 +24,42 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Tag(name = "应用成员管理") @RestController @RequestMapping("/api/app/app-user") public class AppUserController extends BaseController { + @Resource private AppUserService appUserService; - @PreAuthorize("hasAuthority('app:appUser:list')") @Operation(summary = "分页查询应用成员") @GetMapping("/page") public ApiResult> page(AppUserParam param) { - // 使用关联查询 return success(appUserService.pageRel(param)); } - @PreAuthorize("hasAuthority('app:appUser:list')") @Operation(summary = "查询全部应用成员") @GetMapping() public ApiResult> list(AppUserParam param) { - // 使用关联查询 return success(appUserService.listRel(param)); } - @PreAuthorize("hasAuthority('app:appUser:list')") @Operation(summary = "根据id查询应用成员") @GetMapping("/{id}") public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 return success(appUserService.getByIdRel(id)); } @PreAuthorize("hasAuthority('app:appUser:save')") @OperationLog - @Operation(summary = "添加应用成员") + @Operation(summary = "添加应用成员(手动添加)") @PostMapping() public ApiResult save(@RequestBody AppUser appUser) { - // 记录当前登录用户id User loginUser = getLoginUser(); if (loginUser != null) { - appUser.setUserId(loginUser.getUserId()); + appUser.setUserId(loginUser.getUserId()); + appUser.setTenantId(loginUser.getTenantId()); } if (appUserService.save(appUser)) { return success("添加成功"); @@ -71,9 +67,31 @@ public class AppUserController extends BaseController { return fail("添加失败"); } + @PreAuthorize("hasAuthority('app:appUser:save')") + @OperationLog + @Operation(summary = "邀请用户成为应用成员") + @PostMapping("/invite") + public ApiResult invite(@RequestBody AppUser appUser) { + User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("请先登录"); + } + try { + AppUser result = appUserService.inviteUser( + appUser.getWebsiteId(), + appUser.getUserId(), + appUser.getRole(), + loginUser.getUserId() + ); + return success("邀请成功", result); + } catch (RuntimeException e) { + return fail(e.getMessage()); + } + } + @PreAuthorize("hasAuthority('app:appUser:update')") @OperationLog - @Operation(summary = "修改应用成员") + @Operation(summary = "修改应用成员信息") @PutMapping() public ApiResult update(@RequestBody AppUser appUser) { if (appUserService.updateById(appUser)) { @@ -82,15 +100,26 @@ public class AppUserController extends BaseController { return fail("修改失败"); } + @PreAuthorize("hasAuthority('app:appUser:update')") + @OperationLog + @Operation(summary = "修改成员角色") + @PutMapping("/role/{id}/{role}") + public ApiResult updateRole(@PathVariable("id") Long id, @PathVariable("role") String role) { + if (appUserService.updateRole(id, role)) { + return success("角色修改成功"); + } + return fail("修改失败"); + } + @PreAuthorize("hasAuthority('app:appUser:remove')") @OperationLog - @Operation(summary = "删除应用成员") + @Operation(summary = "移除应用成员") @DeleteMapping("/{id}") public ApiResult remove(@PathVariable("id") Integer id) { if (appUserService.removeById(id)) { - return success("删除成功"); + return success("已移除"); } - return fail("删除失败"); + return fail("移除失败"); } @PreAuthorize("hasAuthority('app:appUser:save')") @@ -108,7 +137,7 @@ public class AppUserController extends BaseController { @OperationLog @Operation(summary = "批量修改应用成员") @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + public ApiResult updateBatch(@RequestBody BatchParam batchParam) { if (batchParam.update(appUserService, "id")) { return success("修改成功"); } @@ -117,13 +146,13 @@ public class AppUserController extends BaseController { @PreAuthorize("hasAuthority('app:appUser:remove')") @OperationLog - @Operation(summary = "批量删除应用成员") + @Operation(summary = "批量移除应用成员") @DeleteMapping("/batch") public ApiResult removeBatch(@RequestBody List ids) { if (appUserService.removeByIds(ids)) { - return success("删除成功"); + return success("移除成功"); } - return fail("删除失败"); + return fail("移除失败"); } } diff --git a/src/main/java/com/gxwebsoft/app/controller/AppVersionController.java b/src/main/java/com/gxwebsoft/app/controller/AppVersionController.java index c5e8ad2..50633fa 100644 --- a/src/main/java/com/gxwebsoft/app/controller/AppVersionController.java +++ b/src/main/java/com/gxwebsoft/app/controller/AppVersionController.java @@ -6,12 +6,12 @@ import com.gxwebsoft.app.entity.AppVersion; import com.gxwebsoft.app.param.AppVersionParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.system.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -24,56 +24,66 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ -@Tag(name = "应用版本发布记录管理") +@Slf4j +@Tag(name = "应用版本发布管理") @RestController @RequestMapping("/api/app/app-version") public class AppVersionController extends BaseController { + @Resource private AppVersionService appVersionService; - @PreAuthorize("hasAuthority('app:appVersion:list')") - @Operation(summary = "分页查询应用版本发布记录") + @Operation(summary = "分页查询版本记录") @GetMapping("/page") public ApiResult> page(AppVersionParam param) { - // 使用关联查询 return success(appVersionService.pageRel(param)); } - @PreAuthorize("hasAuthority('app:appVersion:list')") - @Operation(summary = "查询全部应用版本发布记录") + @Operation(summary = "查询全部版本记录") @GetMapping() public ApiResult> list(AppVersionParam param) { - // 使用关联查询 return success(appVersionService.listRel(param)); } - @PreAuthorize("hasAuthority('app:appVersion:list')") - @Operation(summary = "根据id查询应用版本发布记录") + @Operation(summary = "根据id查询版本") @GetMapping("/{id}") public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 return success(appVersionService.getByIdRel(id)); } + @Operation(summary = "获取应用当前版本") + @GetMapping("/current/{websiteId}") + public ApiResult getCurrentVersion(@PathVariable("websiteId") Long websiteId) { + return success(appVersionService.getCurrentVersion(websiteId)); + } + @PreAuthorize("hasAuthority('app:appVersion:save')") @OperationLog - @Operation(summary = "添加应用版本发布记录") + @Operation(summary = "新增版本(构建中状态)") @PostMapping() public ApiResult save(@RequestBody AppVersion appVersion) { - // 记录当前登录用户id User loginUser = getLoginUser(); if (loginUser != null) { - appVersion.setUserId(loginUser.getUserId()); + appVersion.setUserId(loginUser.getUserId()); + appVersion.setTenantId(loginUser.getTenantId()); } + // 默认为构建中状态 + if (appVersion.getStatus() == null) { + appVersion.setStatus(0); + } + if (appVersion.getEnv() == null) { + appVersion.setEnv("production"); + } + appVersion.setIsCurrent(false); if (appVersionService.save(appVersion)) { - return success("添加成功"); + return success("创建成功"); } - return fail("添加失败"); + return fail("创建失败"); } @PreAuthorize("hasAuthority('app:appVersion:update')") @OperationLog - @Operation(summary = "修改应用版本发布记录") + @Operation(summary = "修改版本信息") @PutMapping() public ApiResult update(@RequestBody AppVersion appVersion) { if (appVersionService.updateById(appVersion)) { @@ -82,9 +92,43 @@ public class AppVersionController extends BaseController { return fail("修改失败"); } + @PreAuthorize("hasAuthority('app:appVersion:update')") + @OperationLog + @Operation(summary = "发布版本(将此版本设为当前运行版本)") + @PostMapping("/publish/{id}") + public ApiResult publish(@PathVariable("id") Long id) { + User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("请先登录"); + } + try { + appVersionService.publish(id, loginUser.getUserId()); + return success("发布成功"); + } catch (RuntimeException e) { + return fail(e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('app:appVersion:update')") + @OperationLog + @Operation(summary = "回滚到指定版本") + @PostMapping("/rollback/{id}") + public ApiResult rollback(@PathVariable("id") Long id) { + User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("请先登录"); + } + try { + appVersionService.rollback(id, loginUser.getUserId()); + return success("回滚成功"); + } catch (RuntimeException e) { + return fail(e.getMessage()); + } + } + @PreAuthorize("hasAuthority('app:appVersion:remove')") @OperationLog - @Operation(summary = "删除应用版本发布记录") + @Operation(summary = "删除版本记录") @DeleteMapping("/{id}") public ApiResult remove(@PathVariable("id") Integer id) { if (appVersionService.removeById(id)) { @@ -95,7 +139,7 @@ public class AppVersionController extends BaseController { @PreAuthorize("hasAuthority('app:appVersion:save')") @OperationLog - @Operation(summary = "批量添加应用版本发布记录") + @Operation(summary = "批量添加版本") @PostMapping("/batch") public ApiResult saveBatch(@RequestBody List list) { if (appVersionService.saveBatch(list)) { @@ -106,9 +150,9 @@ public class AppVersionController extends BaseController { @PreAuthorize("hasAuthority('app:appVersion:update')") @OperationLog - @Operation(summary = "批量修改应用版本发布记录") + @Operation(summary = "批量修改版本") @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + public ApiResult updateBatch(@RequestBody BatchParam batchParam) { if (batchParam.update(appVersionService, "id")) { return success("修改成功"); } @@ -117,7 +161,7 @@ public class AppVersionController extends BaseController { @PreAuthorize("hasAuthority('app:appVersion:remove')") @OperationLog - @Operation(summary = "批量删除应用版本发布记录") + @Operation(summary = "批量删除版本记录") @DeleteMapping("/batch") public ApiResult removeBatch(@RequestBody List ids) { if (appVersionService.removeByIds(ids)) { diff --git a/src/main/java/com/gxwebsoft/app/mapper/xml/AppCredentialMapper.xml b/src/main/java/com/gxwebsoft/app/mapper/xml/AppCredentialMapper.xml index 0c2c0c0..5c709ff 100644 --- a/src/main/java/com/gxwebsoft/app/mapper/xml/AppCredentialMapper.xml +++ b/src/main/java/com/gxwebsoft/app/mapper/xml/AppCredentialMapper.xml @@ -4,37 +4,29 @@ - SELECT a.* + SELECT a.*, w.website_name, w.website_code, w.website_icon FROM app_credential a + LEFT JOIN cms_website w ON a.website_id = w.website_id AND w.deleted = 0 AND a.id = #{param.id} - AND a.website_id LIKE CONCAT('%', #{param.websiteId}, '%') + AND a.website_id = #{param.websiteId} - + AND a.name LIKE CONCAT('%', #{param.name}, '%') - - AND a.app_id LIKE CONCAT('%', #{param.appId}, '%') + + AND a.app_id = #{param.appId} - - AND a.app_secret LIKE CONCAT('%', #{param.appSecret}, '%') + + AND a.type = #{param.type} - - AND a.type LIKE CONCAT('%', #{param.type}, '%') - - + AND a.scopes LIKE CONCAT('%', #{param.scopes}, '%') - - AND a.expire_time LIKE CONCAT('%', #{param.expireTime}, '%') - - - AND a.last_used_at LIKE CONCAT('%', #{param.lastUsedAt}, '%') - - + AND a.remark LIKE CONCAT('%', #{param.remark}, '%') @@ -58,10 +50,11 @@ AND a.create_time <= #{param.createTimeEnd} - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + + AND (a.name LIKE CONCAT('%', #{param.keywords}, '%') + OR a.app_id LIKE CONCAT('%', #{param.keywords}, '%') ) - + diff --git a/src/main/java/com/gxwebsoft/app/mapper/xml/AppEventMapper.xml b/src/main/java/com/gxwebsoft/app/mapper/xml/AppEventMapper.xml index ef63c66..8317ac1 100644 --- a/src/main/java/com/gxwebsoft/app/mapper/xml/AppEventMapper.xml +++ b/src/main/java/com/gxwebsoft/app/mapper/xml/AppEventMapper.xml @@ -4,41 +4,30 @@ - SELECT a.* + SELECT a.*, w.website_name, w.website_code, w.website_icon FROM app_event a + LEFT JOIN cms_website w ON a.website_id = w.website_id AND w.deleted = 0 AND a.id = #{param.id} - AND a.website_id LIKE CONCAT('%', #{param.websiteId}, '%') + AND a.website_id = #{param.websiteId} - - AND a.event_type LIKE CONCAT('%', #{param.eventType}, '%') + + AND a.event_type = #{param.eventType} - + AND a.title LIKE CONCAT('%', #{param.title}, '%') - - AND a.content LIKE CONCAT('%', #{param.content}, '%') - - AND a.operator_id LIKE CONCAT('%', #{param.operatorId}, '%') - - - AND a.operator LIKE CONCAT('%', #{param.operator}, '%') + AND a.operator_id = #{param.operatorId} - AND a.ref_id LIKE CONCAT('%', #{param.refId}, '%') + AND a.ref_id = #{param.refId} - - AND a.ref_type LIKE CONCAT('%', #{param.refType}, '%') - - - AND a.extra LIKE CONCAT('%', #{param.extra}, '%') - - - AND a.sort_number = #{param.sortNumber} + + AND a.ref_type = #{param.refType} AND a.status = #{param.status} @@ -46,17 +35,22 @@ AND a.user_id = #{param.userId} + + AND a.tenant_id = #{param.tenantId} + AND a.create_time >= #{param.createTimeStart} AND a.create_time <= #{param.createTimeEnd} - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + + AND (a.title LIKE CONCAT('%', #{param.keywords}, '%') + OR a.content LIKE CONCAT('%', #{param.keywords}, '%') ) - + + ORDER BY a.create_time DESC diff --git a/src/main/java/com/gxwebsoft/app/mapper/xml/AppUserMapper.xml b/src/main/java/com/gxwebsoft/app/mapper/xml/AppUserMapper.xml index e52506a..40ac1e8 100644 --- a/src/main/java/com/gxwebsoft/app/mapper/xml/AppUserMapper.xml +++ b/src/main/java/com/gxwebsoft/app/mapper/xml/AppUserMapper.xml @@ -4,32 +4,30 @@ - SELECT a.* + SELECT a.*, w.website_name, w.website_code, w.website_icon, + u.nickname, u.avatar AS user_avatar, u.phone FROM app_user a + LEFT JOIN cms_website w ON a.website_id = w.website_id AND w.deleted = 0 + LEFT JOIN sys_user u ON a.user_id = u.user_id AND a.id = #{param.id} - AND a.website_id LIKE CONCAT('%', #{param.websiteId}, '%') + AND a.website_id = #{param.websiteId} - AND a.user_id LIKE CONCAT('%', #{param.userId}, '%') + AND a.user_id = #{param.userId} - - AND a.username LIKE CONCAT('%', #{param.username}, '%') + + AND (a.username LIKE CONCAT('%', #{param.username}, '%') + OR u.nickname LIKE CONCAT('%', #{param.username}, '%')) - - AND a.avatar LIKE CONCAT('%', #{param.avatar}, '%') - - - AND a.role LIKE CONCAT('%', #{param.role}, '%') + + AND a.role = #{param.role} - AND a.invite_by LIKE CONCAT('%', #{param.inviteBy}, '%') - - - AND a.invite_time LIKE CONCAT('%', #{param.inviteTime}, '%') + AND a.invite_by = #{param.inviteBy} AND a.sort_number = #{param.sortNumber} @@ -37,16 +35,20 @@ AND a.status = #{param.status} + + AND a.tenant_id = #{param.tenantId} + AND a.create_time >= #{param.createTimeStart} AND a.create_time <= #{param.createTimeEnd} - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + + AND (a.username LIKE CONCAT('%', #{param.keywords}, '%') + OR u.nickname LIKE CONCAT('%', #{param.keywords}, '%') ) - + diff --git a/src/main/java/com/gxwebsoft/app/mapper/xml/AppVersionMapper.xml b/src/main/java/com/gxwebsoft/app/mapper/xml/AppVersionMapper.xml index 23dc680..daf1699 100644 --- a/src/main/java/com/gxwebsoft/app/mapper/xml/AppVersionMapper.xml +++ b/src/main/java/com/gxwebsoft/app/mapper/xml/AppVersionMapper.xml @@ -4,35 +4,24 @@ - SELECT a.* + SELECT a.*, w.website_name, w.website_code, w.website_icon FROM app_version a + LEFT JOIN cms_website w ON a.website_id = w.website_id AND w.deleted = 0 AND a.id = #{param.id} - AND a.website_id LIKE CONCAT('%', #{param.websiteId}, '%') + AND a.website_id = #{param.websiteId} - - AND a.version_no LIKE CONCAT('%', #{param.versionNo}, '%') + + AND a.version_no = #{param.versionNo} - + AND a.version_name LIKE CONCAT('%', #{param.versionName}, '%') - - AND a.changelog LIKE CONCAT('%', #{param.changelog}, '%') - - - AND a.package_url LIKE CONCAT('%', #{param.packageUrl}, '%') - - - AND a.package_size LIKE CONCAT('%', #{param.packageSize}, '%') - - - AND a.package_hash LIKE CONCAT('%', #{param.packageHash}, '%') - - - AND a.env LIKE CONCAT('%', #{param.env}, '%') + + AND a.env = #{param.env} AND a.status = #{param.status} @@ -41,30 +30,26 @@ AND a.is_current = #{param.isCurrent} - AND a.publish_by LIKE CONCAT('%', #{param.publishBy}, '%') - - - AND a.publish_time LIKE CONCAT('%', #{param.publishTime}, '%') - - - AND a.remark LIKE CONCAT('%', #{param.remark}, '%') - - - AND a.sort_number = #{param.sortNumber} + AND a.publish_by = #{param.publishBy} AND a.user_id = #{param.userId} + + AND a.tenant_id = #{param.tenantId} + AND a.create_time >= #{param.createTimeStart} AND a.create_time <= #{param.createTimeEnd} - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + + AND (a.version_no LIKE CONCAT('%', #{param.keywords}, '%') + OR a.version_name LIKE CONCAT('%', #{param.keywords}, '%') + OR a.changelog LIKE CONCAT('%', #{param.keywords}, '%') ) - + diff --git a/src/main/java/com/gxwebsoft/app/param/AppCredentialParam.java b/src/main/java/com/gxwebsoft/app/param/AppCredentialParam.java index cd53d0b..f40632c 100644 --- a/src/main/java/com/gxwebsoft/app/param/AppCredentialParam.java +++ b/src/main/java/com/gxwebsoft/app/param/AppCredentialParam.java @@ -1,6 +1,5 @@ package com.gxwebsoft.app.param; -import java.math.BigDecimal; import com.gxwebsoft.common.core.annotation.QueryField; import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.web.BaseParam; @@ -27,29 +26,24 @@ public class AppCredentialParam extends BaseParam { private Long id; @Schema(description = "关联应用ID") + @QueryField(type = QueryType.EQ) private Long websiteId; @Schema(description = "凭证名称,如生产环境密钥") private String name; @Schema(description = "App ID(公开)") + @QueryField(type = QueryType.EQ) private String appId; - @Schema(description = "App Secret(加密存储)") - private String appSecret; - @Schema(description = "凭证类型: server/client/webhook") + @QueryField(type = QueryType.EQ) private String type; @Schema(description = "权限范围,空格分隔") private String scopes; - @Schema(description = "到期时间,NULL=永不过期") - private String expireTime; - - @Schema(description = "最后使用时间") - private String lastUsedAt; - + @Schema(description = "备注") private String remark; @Schema(description = "排序(数字越小越靠前)") diff --git a/src/main/java/com/gxwebsoft/app/param/AppEventParam.java b/src/main/java/com/gxwebsoft/app/param/AppEventParam.java index 64b2e2a..685d390 100644 --- a/src/main/java/com/gxwebsoft/app/param/AppEventParam.java +++ b/src/main/java/com/gxwebsoft/app/param/AppEventParam.java @@ -1,6 +1,5 @@ package com.gxwebsoft.app.param; -import java.math.BigDecimal; import com.gxwebsoft.common.core.annotation.QueryField; import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.web.BaseParam; @@ -27,35 +26,27 @@ public class AppEventParam extends BaseParam { private Long id; @Schema(description = "关联应用ID") + @QueryField(type = QueryType.EQ) private Long websiteId; @Schema(description = "事件类型: created/published/updated/domain_bound/member_added/status_changed") + @QueryField(type = QueryType.EQ) private String eventType; - @Schema(description = "事件标题,如已发布") + @Schema(description = "事件标题(模糊)") private String title; - @Schema(description = "详细描述") - private String content; - @Schema(description = "操作人用户ID") + @QueryField(type = QueryType.EQ) private Long operatorId; - @Schema(description = "操作人名称(冗余)") - private String operator; - @Schema(description = "关联ID,如版本ID") + @QueryField(type = QueryType.EQ) private Long refId; @Schema(description = "关联类型") - private String refType; - - @Schema(description = "扩展数据") - private String extra; - - @Schema(description = "排序(数字越小越靠前)") @QueryField(type = QueryType.EQ) - private Integer sortNumber; + private String refType; @Schema(description = "状态, 0正常, 1冻结") @QueryField(type = QueryType.EQ) @@ -65,4 +56,8 @@ public class AppEventParam extends BaseParam { @QueryField(type = QueryType.EQ) private Integer userId; + @Schema(description = "租户ID") + @QueryField(type = QueryType.EQ) + private Integer tenantId; + } diff --git a/src/main/java/com/gxwebsoft/app/param/AppUserParam.java b/src/main/java/com/gxwebsoft/app/param/AppUserParam.java index d9578f0..cc6adea 100644 --- a/src/main/java/com/gxwebsoft/app/param/AppUserParam.java +++ b/src/main/java/com/gxwebsoft/app/param/AppUserParam.java @@ -1,6 +1,5 @@ package com.gxwebsoft.app.param; -import java.math.BigDecimal; import com.gxwebsoft.common.core.annotation.QueryField; import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.web.BaseParam; @@ -27,26 +26,24 @@ public class AppUserParam extends BaseParam { private Long id; @Schema(description = "关联应用ID") + @QueryField(type = QueryType.EQ) private Long websiteId; @Schema(description = "用户ID") - private Long userId; + @QueryField(type = QueryType.EQ) + private Integer userId; - @Schema(description = "用户名(冗余)") + @Schema(description = "用户名(模糊搜索)") private String username; - @Schema(description = "头像(冗余)") - private String avatar; - @Schema(description = "角色: owner/admin/developer/viewer") + @QueryField(type = QueryType.EQ) private String role; @Schema(description = "邀请人用户ID") + @QueryField(type = QueryType.EQ) private Long inviteBy; - @Schema(description = "加入时间") - private String inviteTime; - @Schema(description = "排序(数字越小越靠前)") @QueryField(type = QueryType.EQ) private Integer sortNumber; @@ -55,4 +52,8 @@ public class AppUserParam extends BaseParam { @QueryField(type = QueryType.EQ) private Integer status; + @Schema(description = "租户ID") + @QueryField(type = QueryType.EQ) + private Integer tenantId; + } diff --git a/src/main/java/com/gxwebsoft/app/param/AppVersionParam.java b/src/main/java/com/gxwebsoft/app/param/AppVersionParam.java index a986bc0..bf70f1a 100644 --- a/src/main/java/com/gxwebsoft/app/param/AppVersionParam.java +++ b/src/main/java/com/gxwebsoft/app/param/AppVersionParam.java @@ -1,6 +1,5 @@ package com.gxwebsoft.app.param; -import java.math.BigDecimal; import com.gxwebsoft.common.core.annotation.QueryField; import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.web.BaseParam; @@ -27,27 +26,18 @@ public class AppVersionParam extends BaseParam { private Long id; @Schema(description = "关联应用ID") + @QueryField(type = QueryType.EQ) private Long websiteId; @Schema(description = "版本号,如 1.0.0") + @QueryField(type = QueryType.EQ) private String versionNo; - @Schema(description = "版本名称") + @Schema(description = "版本名称(模糊)") private String versionName; - @Schema(description = "版本更新说明") - private String changelog; - - @Schema(description = "安装包地址") - private String packageUrl; - - @Schema(description = "包大小(字节)") - private Long packageSize; - - @Schema(description = "包MD5/SHA256") - private String packageHash; - @Schema(description = "环境: development/staging/production") + @QueryField(type = QueryType.EQ) private String env; @Schema(description = "状态 0=构建中 1=已发布 2=已回滚 3=构建失败") @@ -59,20 +49,15 @@ public class AppVersionParam extends BaseParam { private Boolean isCurrent; @Schema(description = "发布人用户ID") - private Long publishBy; - - @Schema(description = "发布时间") - private String publishTime; - - @Schema(description = "备注") - private String remark; - - @Schema(description = "排序(数字越小越靠前)") @QueryField(type = QueryType.EQ) - private Integer sortNumber; + private Long publishBy; @Schema(description = "用户ID") @QueryField(type = QueryType.EQ) private Integer userId; + @Schema(description = "租户ID") + @QueryField(type = QueryType.EQ) + private Integer tenantId; + } diff --git a/src/main/java/com/gxwebsoft/app/service/AppCredentialService.java b/src/main/java/com/gxwebsoft/app/service/AppCredentialService.java index 6a426ce..ddd5d66 100644 --- a/src/main/java/com/gxwebsoft/app/service/AppCredentialService.java +++ b/src/main/java/com/gxwebsoft/app/service/AppCredentialService.java @@ -17,26 +17,41 @@ public interface AppCredentialService extends IService { /** * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult */ PageResult pageRel(AppCredentialParam param); /** * 关联查询全部 - * - * @param param 查询参数 - * @return List */ List listRel(AppCredentialParam param); /** * 根据id查询 - * - * @param id 自增ID - * @return AppCredential */ AppCredential getByIdRel(Integer id); + /** + * 创建凭证(自动生成 appId 和 appSecret) + * + * @param credential 凭证基础信息 + * @return 包含明文 appSecret 的凭证对象(仅此次返回,之后不再展示) + */ + AppCredential createCredential(AppCredential credential); + + /** + * 重置密钥(重新生成 appSecret) + * + * @param id 凭证ID + * @return 包含新明文 appSecret 的凭证对象 + */ + AppCredential resetSecret(Long id); + + /** + * 禁用/启用凭证 + * + * @param id 凭证ID + * @param status 0=正常 1=冻结 + */ + boolean updateStatus(Long id, Integer status); + } diff --git a/src/main/java/com/gxwebsoft/app/service/AppEventService.java b/src/main/java/com/gxwebsoft/app/service/AppEventService.java index 34d8d89..d0a8b0f 100644 --- a/src/main/java/com/gxwebsoft/app/service/AppEventService.java +++ b/src/main/java/com/gxwebsoft/app/service/AppEventService.java @@ -17,26 +17,44 @@ public interface AppEventService extends IService { /** * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult */ PageResult pageRel(AppEventParam param); /** * 关联查询全部 - * - * @param param 查询参数 - * @return List */ List listRel(AppEventParam param); /** * 根据id查询 - * - * @param id 自增ID - * @return AppEvent */ AppEvent getByIdRel(Integer id); + /** + * 记录应用事件(便捷方法,供其他模块调用) + * + * @param websiteId 应用ID + * @param eventType 事件类型(created/published/updated/domain_bound/member_added/status_changed 等) + * @param title 事件标题 + * @param content 详细描述 + * @param operatorId 操作人用户ID + * @param operatorName 操作人名称 + * @param refId 关联记录ID(如版本ID) + * @param refType 关联记录类型 + */ + void logEvent(Long websiteId, String eventType, String title, String content, + Long operatorId, String operatorName, Long refId, String refType); + + /** + * 快捷记录事件(无关联记录) + */ + void logEvent(Long websiteId, String eventType, String title, Long operatorId, String operatorName); + + /** + * 获取指定应用的最新一条事件(用于卡片"最新动态"展示) + * + * @param websiteId 应用ID + */ + AppEvent getLatestEvent(Long websiteId); + } diff --git a/src/main/java/com/gxwebsoft/app/service/AppUserService.java b/src/main/java/com/gxwebsoft/app/service/AppUserService.java index 82706e9..6888fc2 100644 --- a/src/main/java/com/gxwebsoft/app/service/AppUserService.java +++ b/src/main/java/com/gxwebsoft/app/service/AppUserService.java @@ -17,26 +17,44 @@ public interface AppUserService extends IService { /** * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult */ PageResult pageRel(AppUserParam param); /** * 关联查询全部 - * - * @param param 查询参数 - * @return List */ List listRel(AppUserParam param); /** * 根据id查询 - * - * @param id 自增ID - * @return AppUser */ AppUser getByIdRel(Integer id); + /** + * 邀请成员加入应用 + * + * @param websiteId 应用ID + * @param userId 被邀请的用户ID + * @param role 分配的角色 + * @param inviteBy 邀请人用户ID + * @return 成员记录 + */ + AppUser inviteUser(Long websiteId, Integer userId, String role, Integer inviteBy); + + /** + * 修改成员角色 + * + * @param id 成员记录ID + * @param role 新角色 + */ + boolean updateRole(Long id, String role); + + /** + * 检查用户是否已是应用成员 + * + * @param websiteId 应用ID + * @param userId 用户ID + */ + boolean isMember(Long websiteId, Integer userId); + } diff --git a/src/main/java/com/gxwebsoft/app/service/AppVersionService.java b/src/main/java/com/gxwebsoft/app/service/AppVersionService.java index 8a56e0e..bbfa879 100644 --- a/src/main/java/com/gxwebsoft/app/service/AppVersionService.java +++ b/src/main/java/com/gxwebsoft/app/service/AppVersionService.java @@ -17,26 +17,40 @@ public interface AppVersionService extends IService { /** * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult */ PageResult pageRel(AppVersionParam param); /** * 关联查询全部 - * - * @param param 查询参数 - * @return List */ List listRel(AppVersionParam param); /** * 根据id查询 - * - * @param id 自增ID - * @return AppVersion */ AppVersion getByIdRel(Integer id); + /** + * 发布版本(将此版本设为当前版本,其他版本 isCurrent=false) + * + * @param id 版本ID + * @param publishBy 发布人用户ID + */ + boolean publish(Long id, Integer publishBy); + + /** + * 回滚到指定版本(将此版本设为当前版本,当前版本标为已回滚) + * + * @param id 要回滚到的版本ID + * @param publishBy 操作人用户ID + */ + boolean rollback(Long id, Integer publishBy); + + /** + * 获取指定应用的当前版本 + * + * @param websiteId 应用ID + */ + AppVersion getCurrentVersion(Long websiteId); + } diff --git a/src/main/java/com/gxwebsoft/app/service/impl/AppCredentialServiceImpl.java b/src/main/java/com/gxwebsoft/app/service/impl/AppCredentialServiceImpl.java index 3028fde..cf2de6b 100644 --- a/src/main/java/com/gxwebsoft/app/service/impl/AppCredentialServiceImpl.java +++ b/src/main/java/com/gxwebsoft/app/service/impl/AppCredentialServiceImpl.java @@ -1,12 +1,17 @@ package com.gxwebsoft.app.service.impl; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.app.mapper.AppCredentialMapper; import com.gxwebsoft.app.service.AppCredentialService; import com.gxwebsoft.app.entity.AppCredential; import com.gxwebsoft.app.param.AppCredentialParam; +import com.gxwebsoft.common.core.utils.CommonUtil; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @@ -17,6 +22,7 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:43 */ +@Slf4j @Service public class AppCredentialServiceImpl extends ServiceImpl implements AppCredentialService { @@ -25,23 +31,95 @@ public class AppCredentialServiceImpl extends ServiceImpl page = new PageParam<>(param); page.setDefaultOrder("sort_number asc, create_time desc"); List list = baseMapper.selectPageRel(page, param); + // 脱敏处理:appSecret 不对外展示(仅创建/重置时返回明文) + list.forEach(this::maskSecret); return new PageResult<>(list, page.getTotal()); } @Override public List listRel(AppCredentialParam param) { List list = baseMapper.selectListRel(param); - // 排序 PageParam page = new PageParam<>(); page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); + list = page.sortRecords(list); + list.forEach(this::maskSecret); + return list; } @Override public AppCredential getByIdRel(Integer id) { AppCredentialParam param = new AppCredentialParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); + param.setId(id.longValue()); + AppCredential credential = param.getOne(baseMapper.selectListRel(param)); + if (credential != null) { + maskSecret(credential); + } + return credential; + } + + @Override + public AppCredential createCredential(AppCredential credential) { + // 生成 AppID:app_ + 16位随机字符串 + String appId = "app_" + CommonUtil.randomUUID16(); + // 生成 AppSecret:32位随机字符串(大小写字母+数字) + String appSecret = generateSecretValue(); + + credential.setAppId(appId); + credential.setAppSecret(appSecret); + // 默认状态正常 + if (credential.getStatus() == null) { + credential.setStatus(0); + } + if (StrUtil.isBlank(credential.getType())) { + credential.setType("server"); + } + + save(credential); + log.info("创建凭证成功,websiteId={}, appId={}", credential.getWebsiteId(), appId); + // 返回含明文 secret 的对象(仅此一次) + return credential; + } + + @Override + public AppCredential resetSecret(Long id) { + AppCredential credential = getById(id); + if (credential == null) { + throw new RuntimeException("凭证不存在"); + } + String newSecret = generateSecretValue(); + credential.setAppSecret(newSecret); + updateById(credential); + log.info("重置凭证密钥成功,id={}", id); + return credential; + } + + @Override + public boolean updateStatus(Long id, Integer status) { + return update(new LambdaUpdateWrapper() + .eq(AppCredential::getId, id) + .set(AppCredential::getStatus, status)); + } + + /** + * 生成32位随机 AppSecret + */ + private String generateSecretValue() { + // 格式:8位-8位-8位-8位(类似 UUID 格式,便于复制) + return RandomUtil.randomString(8) + "-" + + RandomUtil.randomString(8) + "-" + + RandomUtil.randomString(8) + "-" + + RandomUtil.randomString(8); + } + + /** + * 脱敏处理:将 appSecret 替换为掩码 + */ + private void maskSecret(AppCredential credential) { + if (StrUtil.isNotBlank(credential.getAppSecret())) { + // 只保留前4位,其余用 * 替代 + String secret = credential.getAppSecret(); + credential.setAppSecret(secret.substring(0, Math.min(4, secret.length())) + "**********************"); + } } } diff --git a/src/main/java/com/gxwebsoft/app/service/impl/AppEventServiceImpl.java b/src/main/java/com/gxwebsoft/app/service/impl/AppEventServiceImpl.java index 91c147a..bcdb7c8 100644 --- a/src/main/java/com/gxwebsoft/app/service/impl/AppEventServiceImpl.java +++ b/src/main/java/com/gxwebsoft/app/service/impl/AppEventServiceImpl.java @@ -1,5 +1,6 @@ package com.gxwebsoft.app.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.app.mapper.AppEventMapper; import com.gxwebsoft.app.service.AppEventService; @@ -7,6 +8,8 @@ import com.gxwebsoft.app.entity.AppEvent; import com.gxwebsoft.app.param.AppEventParam; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.List; @@ -17,13 +20,14 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Service public class AppEventServiceImpl extends ServiceImpl implements AppEventService { @Override public PageResult pageRel(AppEventParam param) { PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("create_time desc"); List list = baseMapper.selectPageRel(page, param); return new PageResult<>(list, page.getTotal()); } @@ -31,17 +35,52 @@ public class AppEventServiceImpl extends ServiceImpl i @Override public List listRel(AppEventParam param) { List list = baseMapper.selectListRel(param); - // 排序 PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("create_time desc"); return page.sortRecords(list); } @Override public AppEvent getByIdRel(Integer id) { AppEventParam param = new AppEventParam(); - param.setId(id); + param.setId(id.longValue()); return param.getOne(baseMapper.selectListRel(param)); } + @Override + @Async + public void logEvent(Long websiteId, String eventType, String title, String content, + Long operatorId, String operatorName, Long refId, String refType) { + try { + AppEvent event = new AppEvent(); + event.setWebsiteId(websiteId); + event.setEventType(eventType); + event.setTitle(title); + event.setContent(content); + event.setOperatorId(operatorId); + event.setOperator(operatorName); + event.setRefId(refId); + event.setRefType(refType); + event.setStatus(0); + save(event); + log.debug("记录事件成功,websiteId={}, eventType={}, title={}", websiteId, eventType, title); + } catch (Exception e) { + log.warn("记录事件失败,websiteId={}, eventType={}: {}", websiteId, eventType, e.getMessage()); + } + } + + @Override + @Async + public void logEvent(Long websiteId, String eventType, String title, Long operatorId, String operatorName) { + logEvent(websiteId, eventType, title, null, operatorId, operatorName, null, null); + } + + @Override + public AppEvent getLatestEvent(Long websiteId) { + return getOne(new LambdaQueryWrapper() + .eq(AppEvent::getWebsiteId, websiteId) + .orderByDesc(AppEvent::getCreateTime) + .last("LIMIT 1")); + } + } diff --git a/src/main/java/com/gxwebsoft/app/service/impl/AppUserServiceImpl.java b/src/main/java/com/gxwebsoft/app/service/impl/AppUserServiceImpl.java index 2f2fb01..47ff990 100644 --- a/src/main/java/com/gxwebsoft/app/service/impl/AppUserServiceImpl.java +++ b/src/main/java/com/gxwebsoft/app/service/impl/AppUserServiceImpl.java @@ -1,5 +1,8 @@ package com.gxwebsoft.app.service.impl; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.app.mapper.AppUserMapper; import com.gxwebsoft.app.service.AppUserService; @@ -7,8 +10,10 @@ import com.gxwebsoft.app.entity.AppUser; import com.gxwebsoft.app.param.AppUserParam; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.List; /** @@ -17,13 +22,14 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Service public class AppUserServiceImpl extends ServiceImpl implements AppUserService { @Override public PageResult pageRel(AppUserParam param) { PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("sort_number asc, create_time asc"); List list = baseMapper.selectPageRel(page, param); return new PageResult<>(list, page.getTotal()); } @@ -31,17 +37,53 @@ public class AppUserServiceImpl extends ServiceImpl impl @Override public List listRel(AppUserParam param) { List list = baseMapper.selectListRel(param); - // 排序 PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("sort_number asc, create_time asc"); return page.sortRecords(list); } @Override public AppUser getByIdRel(Integer id) { AppUserParam param = new AppUserParam(); - param.setId(id); + param.setId(id.longValue()); return param.getOne(baseMapper.selectListRel(param)); } + @Override + public AppUser inviteUser(Long websiteId, Integer userId, String role, Integer inviteBy) { + // 检查是否已经是成员 + if (isMember(websiteId, userId)) { + throw new RuntimeException("该用户已经是应用成员"); + } + + AppUser appUser = new AppUser(); + appUser.setWebsiteId(websiteId); + appUser.setUserId(userId); + appUser.setRole(role != null ? role : "developer"); + appUser.setInviteBy(inviteBy.longValue()); + appUser.setInviteTime(LocalDateTime.now()); + appUser.setStatus(0); + appUser.setSortNumber(0); + + save(appUser); + log.info("邀请成员成功,websiteId={}, userId={}, role={}", websiteId, userId, role); + return appUser; + } + + @Override + public boolean updateRole(Long id, String role) { + return update(new LambdaUpdateWrapper() + .eq(AppUser::getId, id) + .set(AppUser::getRole, role)); + } + + @Override + public boolean isMember(Long websiteId, Integer userId) { + long count = count(new LambdaQueryWrapper() + .eq(AppUser::getWebsiteId, websiteId) + .eq(AppUser::getUserId, userId) + .eq(AppUser::getStatus, 0)); + return count > 0; + } + } diff --git a/src/main/java/com/gxwebsoft/app/service/impl/AppVersionServiceImpl.java b/src/main/java/com/gxwebsoft/app/service/impl/AppVersionServiceImpl.java index 3a5e90b..b45446f 100644 --- a/src/main/java/com/gxwebsoft/app/service/impl/AppVersionServiceImpl.java +++ b/src/main/java/com/gxwebsoft/app/service/impl/AppVersionServiceImpl.java @@ -1,5 +1,7 @@ package com.gxwebsoft.app.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.app.mapper.AppVersionMapper; import com.gxwebsoft.app.service.AppVersionService; @@ -7,8 +9,11 @@ import com.gxwebsoft.app.entity.AppVersion; import com.gxwebsoft.app.param.AppVersionParam; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.List; /** @@ -17,13 +22,14 @@ import java.util.List; * @author 科技小王子 * @since 2026-03-28 21:29:44 */ +@Slf4j @Service public class AppVersionServiceImpl extends ServiceImpl implements AppVersionService { @Override public PageResult pageRel(AppVersionParam param) { PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("create_time desc"); List list = baseMapper.selectPageRel(page, param); return new PageResult<>(list, page.getTotal()); } @@ -31,17 +37,76 @@ public class AppVersionServiceImpl extends ServiceImpl listRel(AppVersionParam param) { List list = baseMapper.selectListRel(param); - // 排序 PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); + page.setDefaultOrder("create_time desc"); return page.sortRecords(list); } @Override public AppVersion getByIdRel(Integer id) { AppVersionParam param = new AppVersionParam(); - param.setId(id); + param.setId(id.longValue()); return param.getOne(baseMapper.selectListRel(param)); } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean publish(Long id, Integer publishBy) { + AppVersion version = getById(id); + if (version == null) { + throw new RuntimeException("版本不存在"); + } + Long websiteId = version.getWebsiteId(); + + // 1. 将该应用下所有版本的 isCurrent 设为 false + update(new LambdaUpdateWrapper() + .eq(AppVersion::getWebsiteId, websiteId) + .set(AppVersion::getIsCurrent, false)); + + // 2. 将当前版本设为已发布+当前版本 + version.setStatus(1); + version.setIsCurrent(true); + version.setPublishBy(publishBy.longValue()); + version.setPublishTime(LocalDateTime.now()); + + boolean result = updateById(version); + log.info("发布版本成功,id={}, versionNo={}", id, version.getVersionNo()); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean rollback(Long id, Integer publishBy) { + AppVersion targetVersion = getById(id); + if (targetVersion == null) { + throw new RuntimeException("目标版本不存在"); + } + Long websiteId = targetVersion.getWebsiteId(); + + // 1. 将当前版本标记为已回滚 + update(new LambdaUpdateWrapper() + .eq(AppVersion::getWebsiteId, websiteId) + .eq(AppVersion::getIsCurrent, true) + .set(AppVersion::getStatus, 2) // 2=已回滚 + .set(AppVersion::getIsCurrent, false)); + + // 2. 将目标版本设为当前版本 + targetVersion.setStatus(1); + targetVersion.setIsCurrent(true); + targetVersion.setPublishBy(publishBy.longValue()); + targetVersion.setPublishTime(LocalDateTime.now()); + + boolean result = updateById(targetVersion); + log.info("回滚版本成功,id={}, versionNo={}", id, targetVersion.getVersionNo()); + return result; + } + + @Override + public AppVersion getCurrentVersion(Long websiteId) { + return getOne(new LambdaQueryWrapper() + .eq(AppVersion::getWebsiteId, websiteId) + .eq(AppVersion::getIsCurrent, true) + .last("LIMIT 1")); + } + }