Compare commits

...

248 Commits

Author SHA1 Message Date
amypeng dcae975112 Merge branch 'master' of https://gitlink.org.cn/Gitlink/microservices 2026-07-31 09:20:51 +08:00
otto c641ae6ab2 Merge pull request '通过定时任务定时统计专区数据,优化统计接口加载较慢的问题' (#12) from otto/microservices:bug_fix into master 2026-07-29 16:01:17 +08:00
otto 7e5127b078 feat(超算专区): 通过定时任务定时统计专区数据,优化统计接口加载较慢的问题 2026-07-29 16:00:48 +08:00
otto 0e13149981 Merge pull request 'feat(超算专区): 修复超算专区首页统计数据加载很慢的问题' (#9) from otto/microservices:bug_fix into master 2026-07-28 16:17:43 +08:00
otto fd0d726a33 feat(超算专区): 修复超算专区首页统计数据加载很慢的问题 2026-07-28 16:02:38 +08:00
otto 58387872f0 Merge pull request '解决当前公开的会员总览接口当会员数过大时加载缓慢的问题' (#8) from otto/microservices:master into master 2026-07-24 09:34:14 +08:00
otto b62c7a6164 feat(超算专区): 为解决当前公开的会员总览接口当会员数过大时加载缓慢的问题,新增获取会员分类列表公开接口和获取会员列表公开接口 2026-07-24 09:27:15 +08:00
otto 62d4418679 feat(特色专区): 优化会员搜索功能
支持模糊搜索会员的分类和等级
2026-07-16 15:38:52 +08:00
otto ca4c8bb614 feat(特色专区): 优化会员搜索功能
支持模糊搜索会员的分类和等级
2026-07-16 15:36:42 +08:00
amypeng 2a0d9c89e7 积分文章 远程查询无数据处理
积分文章 远程查询无数据处理
2026-06-25 11:59:46 +08:00
otto bbe9fa517e Merge pull request '当前积分置顶文章已被删除 返回null' (#1180) from amypeng/microservices:master into master 2026-06-25 11:06:35 +08:00
amypeng 3cf58c94d9 当前积分置顶文章已被删除 返回null
当前积分置顶文章已被删除 返回null
2026-06-25 11:04:56 +08:00
otto d1040faf52 Merge pull request '超算积分 置顶文章过滤掉已删除数据' (#1179) from amypeng/microservices:master into master 2026-06-24 14:46:13 +08:00
amypeng 49a58bed6c 积分置顶文章 过滤掉被删除的数据
积分置顶文章 过滤掉被删除的数据
2026-06-24 14:37:15 +08:00
otto 76f865624e fix(超算专区): 优化获取项目列表接口性能 2026-06-22 14:20:58 +08:00
otto 385dd55c25 Merge pull request '优化获取项目列表接口性能' (#1178) from otto/microservices:master into master 2026-06-22 14:20:58 +08:00
otto 9d1d5527c8 Merge branch 'master' of code.gitlink.org.cn:Gitlink/microservices 2026-06-18 14:24:51 +08:00
otto 47e24de4fb Merge pull request 'fix(超算专区): 修复专区栏目需要删除两次才能删除的问题' (#1177) from otto/microservices:master into master 2026-06-18 14:24:28 +08:00
otto 7a44a2113d fix(超算专区): 修复专区栏目需要删除两次才能删除的问题 2026-06-18 14:24:08 +08:00
otto 1bf9522d8f Merge pull request 'fix(超算专区): 修复当专区存在审核未通过的相同文章名称时,会导致异常报错' (#1176) from otto/microservices:master into master 2026-06-18 10:44:40 +08:00
otto 5aa499f4de fix(超算专区): 修复当专区存在审核未通过的相同文章名称时,会导致异常报错 2026-06-18 10:42:07 +08:00
otto 35d5b1318e Merge pull request 'fix(超算专区): 修复专区栏目需要删除两次才能删除的问题' (#1175) from otto/microservices:master into master 2026-06-18 09:36:14 +08:00
otto 4ccc743bd2 Merge branch 'master' of code.gitlink.org.cn:Gitlink/microservices 2026-06-18 09:30:42 +08:00
otto 84f624fdb7 fix(超算专区): 修复专区栏目需要删除两次才能删除的问题 2026-06-18 09:27:33 +08:00
otto e43fb391fa feat(超算专区): 优化专区项目并发初始化逻辑 2026-06-16 17:19:06 +08:00
otto fc9e511aa0 Merge pull request '置顶项目信息新增url字段' (#1174) from amypeng/microservices:master into master 2026-06-16 09:28:53 +08:00
amypeng 0abc43f033 置顶项目信息新增url字段
置顶项目信息新增url字段
2026-06-16 09:03:52 +08:00
amypeng 9fe4c69487 置顶项目信息新增url字段 2026-06-16 08:49:51 +08:00
otto d2d2f7368c Merge pull request '超算置顶新闻查询条件修改' (#1173) from amypeng/microservices:master into master 2026-06-15 16:50:36 +08:00
amypeng 77125630e5 超算置顶新闻查询条件修改
超算置顶新闻查询条件修改
2026-06-15 16:47:58 +08:00
amypeng ab53fa6ee0 积分置顶文章 查询增加过期时间条件+去除冗余代码
积分置顶文章 查询增加过期时间条件+去除冗余代码
2026-06-15 16:43:24 +08:00
otto 45f77beb73 Merge pull request '超算积分 每日登录重复检查以及积分置顶新闻接口修改' (#1172) from amypeng/microservices:master into master 2026-06-15 15:50:23 +08:00
amypeng 62c56097e2 超算置顶新闻修改
超算置顶新闻修改
2026-06-15 15:38:47 +08:00
amypeng 66c8132fe3 积分置顶新闻接口修改
积分置顶新闻接口修改
2026-06-15 15:28:50 +08:00
amypeng f0725c78a1 Revert "首页推荐文章"
This reverts commit 2a075ab571.
2026-06-15 11:47:00 +08:00
amypeng 7093cda7bb 每日登录 增加重复校验
每日登录 增加重复校验
2026-06-15 10:36:11 +08:00
amypeng 2a075ab571 首页推荐文章
首页推荐文章
2026-06-12 17:36:24 +08:00
otto eba2082e2b Merge pull request 'fix(超算专区): 编辑已拒绝的文章操作时重新开启对应PR' (#1170) from otto/microservices:master into master 2026-06-12 16:17:46 +08:00
otto 2cf4762b2c Merge remote-tracking branch 'master/master' 2026-06-12 16:17:11 +08:00
otto b43c6384d3 Merge branch 'chaosuan_zone' of code.gitlink.org.cn:otto/microservices 2026-06-12 16:16:57 +08:00
otto 4d378d560d Merge pull request '更新积分中心' (#1169) from otto/microservices:master into master 2026-06-12 15:30:45 +08:00
otto 6006440878 fix(超算专区): 编辑已拒绝的文章操作时重新开启对应PR 2026-06-12 15:25:14 +08:00
otto 4bbd80f739 Merge pull request '测试用例详情接口未返回测试用例标识' (#1167) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-12 14:09:20 +08:00
otto 06633ff0a8 fix(超算专区): 测试用例详情接口未返回测试用例标识 2026-06-12 14:08:59 +08:00
otto 6688c1f0e1 Merge pull request '修复未通过文章编辑概览、头图时未变为待审核状态' (#1166) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-12 13:59:07 +08:00
otto 93fb7eacf0 fix(超算专区): 修复未通过文章编辑概览、头图时未变为待审核状态 2026-06-12 13:58:08 +08:00
amypeng 5d6a64a4cc 定时任务 清楚过期置顶数据
定时任务 清楚过期置顶数据
2026-06-12 00:04:37 +08:00
otto 887f4d1616 Merge pull request '当操作用户身份变更的同时有其他操作写入时,可能导致清空的用户身份缓存被并发的其他线程写入' (#1165) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-11 17:30:29 +08:00
otto 49576e5ad0 fix(超算专区): 当操作用户身份变更的同时有其他操作写入时,可能导致清空的用户身份缓存被并发的其他线程写入 2026-06-11 17:30:12 +08:00
amypeng 666c9c7b5e 无需审核时 资源新增触发积分
无需审核时 资源新增触发积分
2026-06-10 15:41:39 +08:00
amypeng 95e5178de8 积分流水排序
积分流水排序
2026-06-10 11:34:50 +08:00
amypeng 8ce9a96fee 项目置顶 项目名称处理
项目置顶 项目名称处理
2026-06-10 11:06:46 +08:00
amypeng 43572e7698 积分置顶过期时间 精确到时分秒
积分置顶过期时间 精确到时分秒
2026-06-10 10:49:52 +08:00
amypeng 3a5c9e03cf fix: 143945 积分明细的描述调整
积分明细的描述调整
2026-06-10 10:22:11 +08:00
amypeng bb2f414aa4 积分置顶查询接口
积分置顶查询接口移动到open
2026-06-09 13:51:54 +08:00
amypeng 76fa1f5447 积分 fix
积分 fix
2026-06-08 19:08:54 +08:00
amypeng 5e20807aed 积分 项目置顶
积分 项目置顶
2026-06-08 19:02:06 +08:00
amypeng 83566e947a 获取置顶文章
获取置顶文章
2026-06-08 16:05:02 +08:00
amypeng f3a718d75a 积分 项目 关联
积分 项目 关联
2026-06-08 15:27:27 +08:00
amypeng 7c6614bddb Merge branch 'chaosuan_zone' of https://gitlink.org.cn/Gitlink/microservices into chaosuan_zone 2026-06-08 15:22:46 +08:00
amypeng c2b992c990 积分 项目发布触发
积分 项目发布触发 + 文章浏览积分问题修复
2026-06-08 15:22:38 +08:00
amypeng 0e63d5efcd 积分 修改我的项目
积分 修改我的项目
2026-06-08 14:15:51 +08:00
amypeng 29c09c7091 积分项目置顶
积分项目置顶
2026-06-08 13:59:01 +08:00
amypeng 7506ec7cb2 项目置顶代码
项目置顶代码
2026-06-08 13:49:19 +08:00
amypeng 0132d56eb2 添加积分项目置顶
添加积分项目置顶
2026-06-08 11:36:03 +08:00
amypeng 93edb6b138 查询积分置顶文章
查询积分置顶文章
2026-06-08 11:32:32 +08:00
amypeng 3599ac3d4a 文章置顶
文章置顶
2026-06-08 11:16:22 +08:00
amypeng 44e7ed5605 积分表结构变更
积分表结构变更
2026-06-08 08:56:43 +08:00
amypeng b1d46f9471 合并master 代码修复
合并master 代码修复
2026-06-08 08:51:18 +08:00
amypeng 9b0b8c4362 master 分支合并
master 分支合并
2026-06-05 17:37:32 +08:00
amypeng f9d779704a 积分流水
积分流水
2026-06-05 17:17:59 +08:00
otto 2fa5be38b2 Merge pull request 'fix(超算专区): 修复接口参数异常的问题' (#1164) from otto/microservices:master into master 2026-06-05 15:40:01 +08:00
otto 22f3a8dc6b fix(超算专区): 修复接口参数异常的问题 2026-06-05 15:39:01 +08:00
amypeng df594efd9a 积分需求变动
积分 客户端变更为查看流水
2026-06-05 15:30:10 +08:00
otto d2e0c54cdd Merge pull request '修复使用Gitlink搜索接口批量传入可选项目Id参数过多时Get接口会响应失败的问题' (#1163) from otto/microservices:master into master 2026-06-05 14:27:24 +08:00
otto dc65b2d299 fix(超算专区): 修复使用Gitlink搜索接口批量传入可选项目Id参数过多时Get接口会响应失败的问题 2026-06-05 14:26:06 +08:00
otto d62d474848 Merge branch 'master' of code.gitlink.org.cn:Gitlink/microservices 2026-06-05 14:16:57 +08:00
otto 388423d53c fix(超算专区): 修复当会员过多时使用Get接口批量获取1000+用户会导致接口响应失败 2026-06-05 14:09:43 +08:00
otto adbb5d04db feat(超算专区): 获取文章内容时,当分支参数不存在时默认使用master 2026-06-05 12:08:38 +08:00
otto ee2ae04586 Merge pull request 'feat(超算专区): 获取文章内容时,当分支参数不存在时默认使用master' (#1162) from otto/microservices:master into master 2026-06-05 12:08:21 +08:00
otto 2decec46ee Merge pull request '超算专区' (#1161) from otto/microservices:master into master 2026-06-05 11:43:00 +08:00
otto 4402c972c9 Merge pull request '权限问题修复' (#1160) from otto/microservices:chaosuan_pre into chaosuan_pre 2026-06-05 11:20:57 +08:00
otto 14748b4253 feat(超算专区): 专区项目聚合和资源聚合管理员权限通过审核权限标识判断 2026-06-05 11:21:29 +08:00
otto ac22a5ba15 feat(超算专区): 管理员获取Gitlink项目列表时应该展示项目的审核中状态 2026-06-05 11:20:51 +08:00
otto f80bc0219c feat(超算专区): 获取用户当前专区权限时,需判断用户是否为专区/平台管理员 2026-06-05 10:47:15 +08:00
otto 941f30d6b3 Merge pull request '权限问题修复' (#1159) from otto/microservices:chaosuan_pre into chaosuan_pre 2026-06-05 10:46:59 +08:00
otto 19dd235a26 feat(超算专区): 专区项目聚合和资源聚合管理员权限通过审核权限标识判断 2026-06-05 10:38:08 +08:00
otto dea2c5e4d6 Merge pull request 'feat(超算专区): 打印项目搜索时调用ES的请求及返回相关参数' (#1158) from otto/microservices:chaosuan_pre into chaosuan_pre 2026-06-05 10:01:04 +08:00
otto 5f1e8ddaf9 feat(超算专区): 打印项目搜索时调用ES的请求及返回相关参数 2026-06-05 10:00:51 +08:00
otto 1f3f3546a6 Merge pull request '超算预上线版本' (#1157) from otto/microservices:chaosuan_pre into chaosuan_pre 2026-06-05 09:37:06 +08:00
otto f2d18d8334 feat(超算专区): 优化会员总览列表接口访问速度
优化专区会员服务使用批量查询替代循环单条查询,减少接口调用次数
2026-06-05 09:20:34 +08:00
otto f491cd4753 feat(超算专区): 优化会员总览列表接口访问速度
优化专区会员服务使用批量查询替代循环单条查询,减少接口调用次数

# Conflicts:
#	microservices-modules/microservices-modules-zone/src/main/java/com/microservices/zone/member/service/impl/ZoneMemberServiceImpl.java
2026-06-05 09:20:25 +08:00
otto 7bf2a532b3 fix(超算专区): 弃用公开上传接口 2026-06-05 09:16:56 +08:00
otto 87a79ee7f4 fix(超算专区): 仅当文章审核通过时,浏览才添加浏览数 2026-06-05 09:16:52 +08:00
otto c358ffcfb7 fix(超算专区): 修复栏目不存在时未捕获异常 2026-06-05 09:16:36 +08:00
otto 3e4f489914 fix(超算专区): 修复查询栏目下文章列表(包含内容)接口 2026-06-05 09:14:52 +08:00
otto e559bcc48a Merge pull request 'feat(超算专区): 修复非仓库修改无法触发审核逻辑的问题' (#1156) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-04 16:07:50 +08:00
otto 3b599bed3f feat(超算专区): 修复非仓库修改无法触发审核逻辑的问题 2026-06-04 15:24:12 +08:00
otto 8c68611369 Merge pull request '优化会员总览列表接口访问速度' (#1155) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-04 11:18:19 +08:00
otto 1ef0cdcef2 feat(超算专区): 优化会员总览列表接口访问速度
优化专区会员服务使用批量查询替代循环单条查询,减少接口调用次数
2026-06-04 11:17:09 +08:00
amypeng 75ca1eff4d 积分 证书接口
积分 证书接口
2026-06-04 10:51:58 +08:00
otto 8a69939e84 fix(超算专区): 弃用公开上传接口 2026-06-04 10:05:21 +08:00
otto 77e3a9e0d3 Merge pull request 'fix(超算专区): 仅当文章审核通过时,浏览才添加浏览数' (#1154) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-03 16:04:04 +08:00
otto effaebe0d4 fix(超算专区): 仅当文章审核通过时,浏览才添加浏览数 2026-06-03 15:57:55 +08:00
otto 1a8b537dae fix(超算专区): 修复栏目不存在时未捕获异常 2026-06-03 15:53:31 +08:00
otto 86c3f502f1 fix(超算专区): 修复查询栏目下文章列表(包含内容)接口 2026-06-03 15:47:48 +08:00
otto 0ddac0b423 Merge pull request 'fix(超算专区): 修复查询栏目下文章列表(包含内容)接口' (#1153) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-06-03 15:47:18 +08:00
amypeng 9543d7fa38 查询 用户积分申请
查询 用户积分申请
2026-06-03 11:31:25 +08:00
amypeng e7fcf69c9a 积分申请
积分申请 增加查询条件
2026-06-03 09:21:44 +08:00
amypeng b26081dc17 积分 追加sql字段
积分 追加sql字段
2026-06-02 16:04:22 +08:00
amypeng 21658c6b66 积分 命名调整
积分 命名调整
2026-06-02 15:52:43 +08:00
amypeng 80e7b1f1b3 积分申请
积分申请 添加用户信息返回
2026-06-02 15:38:13 +08:00
amypeng 85b19136a8 积分 fix
积分 fix
2026-06-02 15:00:35 +08:00
amypeng e28691709f 积分流水 冗余字段去除
积分流水 冗余字段去除
2026-06-02 14:59:20 +08:00
amypeng 2bb93099b5 积分申请通过,更新变更原因
申请通过,更新变更原因
2026-06-02 14:50:09 +08:00
amypeng d98998422d 积分申请fix
积分申请fix
2026-06-02 11:22:33 +08:00
amypeng e7478556d4 积分申请通过fix
积分申请通过fix
2026-06-02 10:31:39 +08:00
amypeng 22ac8bec90 积分申请处理fix
积分申请处理fix
2026-06-02 10:28:05 +08:00
amypeng bd34c5beae 文章、资源积分获取文案修改
文章、资源积分获取文案修改
2026-06-02 10:14:58 +08:00
amypeng 33dd51c5f1 积分事件信息调整
积分事件信息调整
2026-06-02 08:58:48 +08:00
amypeng ebe565af2e 文章积分触发 fix
文章积分触发 fix
2026-06-01 15:05:14 +08:00
amypeng 30a3119580 积分代码fix
积分代码fix
2026-05-31 17:06:51 +08:00
amypeng 68673c355a 积分 优化
积分 优化
2026-05-30 00:41:13 +08:00
amypeng 6c1a4589e7 积分 用户角色校验
积分 用户角色校验
2026-05-29 19:13:01 +08:00
amypeng bc4d402c88 积分日志增加
积分日志增加
2026-05-29 18:28:04 +08:00
amypeng 21868ae523 文章积分相关
文章积分相关
2026-05-29 17:44:20 +08:00
amypeng 1ac64877f8 超算专区 积分系统
超算专区 积分系统相关代码提交
2026-05-29 15:38:44 +08:00
otto 9fa1545e11 fix(超算专区): 修复定时同步Forge用户时,Forge返回的数据非JSON格式导致同步失败 2026-05-28 14:48:02 +08:00
otto 845993f7c8 Merge pull request '将访问超算专区页面用户自动加入超算专区会员' (#1145) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-27 08:41:49 +08:00
otto 2edc3bacc7 Merge branch 'chaosuan_bugfix' of code.gitlink.org.cn:otto/microservices into chaosuan_zone
# Conflicts:
#	microservices-modules/microservices-modules-zone/src/main/java/com/microservices/zone/detail/service/impl/ZoneDetailServiceImpl.java
2026-05-26 17:40:01 +08:00
otto fc08a962ce feat(超算专区): 将访问超算专区页面用户自动加入超算专区会员
当用户在指定专区检查专区权限时,将用户自动加入该专区会员(指定专区id通过Nacos配置),允许设置会员默认分类和默认等级
2026-05-26 17:31:08 +08:00
otto 541b43d768 feat(超算专区): 新增不区分栏目获取文章列表公开接口 2026-05-25 15:54:11 +08:00
otto 6bb1b3c6eb Merge pull request '新增不区分栏目获取文章列表公开接口' (#1139) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-25 15:53:49 +08:00
otto b784d44e26 fix(超算专区): 修复测试用例未返回用例追踪参数的bug 2026-05-21 14:49:29 +08:00
otto 012ed2b25b Merge pull request 'fix(超算专区): 修复测试用例未返回用例追踪参数的bug' (#1129) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-21 14:49:00 +08:00
otto 7219a66b5a fix(超算专区): 修复测试用例未返回用例追踪参数的bug 2026-05-21 14:19:20 +08:00
otto 24b117a5a2 Merge pull request 'fix(超算专区): 修复测试用例未返回用例追踪参数的bug' (#1128) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-21 14:18:52 +08:00
otto 590bc076c1 Merge pull request '修复修改文章栏目可能导致出现重复文章的bug' (#1127) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-20 10:47:59 +08:00
otto 41baf01039 fix(超算专区): 修复修改文章栏目可能导致出现重复文章的bug 2026-05-20 10:46:45 +08:00
otto e3d33c0507 Merge pull request '更新文章时返回文章id(若是会员更新文章,返回待审核文章id)' (#1123) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-14 14:23:57 +08:00
otto 3e10ed1b54 feat(超算专区): 更新文章时返回文章id(若是会员更新文章,返回待审核文章id) 2026-05-14 14:22:03 +08:00
otto 883fdfdb75 Merge pull request '修复无法移除专区用户的bug' (#1105) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-09 08:52:08 +08:00
欧涛 da4c832e82 feat(超算专区): 将用户从Forge原项目中移除时,若调用Forge接口发生异常,不抛出异常信息(因为从Forge原项目中移除非必要操作,万一失败不应影响后续流程) 2026-05-09 08:51:38 +08:00
otto 92bea2ec47 Merge pull request '修复无法移除专区用户的bug' (#1104) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-09 08:47:04 +08:00
欧涛 ff38c7a6a0 feat(超算专区): 将用户从Forge原项目中移除时,若调用Forge接口发生异常,不抛出异常信息(因为从Forge原项目中移除非必要操作,万一失败不应影响后续流程) 2026-05-08 17:11:26 +08:00
otto 16fc7fd3ee Merge pull request 'feat(超算专区): 聚合合并请求时应使用PR Number' (#1102) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-08 11:25:08 +08:00
欧涛 f61117526e feat(超算专区): 聚合合并请求时应使用PR Number 2026-05-08 11:22:26 +08:00
otto a00cdb4238 Merge pull request 'bug修复' (#1101) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-05-07 17:08:14 +08:00
欧涛 c61346b4d9 feat(超算专区): 当用户被添加为一级组织或者二级组织的平台管理员时,将该用户添加到特色专区对应组织的Owner团队 2026-05-07 17:06:26 +08:00
欧涛 c734737e32 feat(超算专区): 专区项目聚合获取Gitlink项目列表时当项目已添加则返回项目审核状态 2026-05-07 13:58:40 +08:00
欧涛 fc6d67b1ed fix(超算专区): 项目聚合切换是否需要审核时,需判断是否存在未审核项目 2026-05-07 11:21:46 +08:00
otto 1ea5e87767 Merge pull request 'feat(超算专区): 获取项目聚合列表时,若项目为镜像项目则返回项目的镜像owner' (#1099) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 11:23:07 +08:00
欧涛 f1bb7f7b3f feat(超算专区): 获取项目聚合列表时,若项目为镜像项目则返回项目的镜像owner 2026-04-29 11:22:44 +08:00
otto 0f97696614 Merge pull request 'fix(超算专区): 平台管理员没有前台访问权限' (#1098) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 11:06:17 +08:00
欧涛 fcfd415fdc fix(超算专区): 平台管理员没有前台访问权限 2026-04-29 11:05:45 +08:00
otto e12afda308 Merge pull request 'fix(超算专区): 平台管理员没有前台访问权限' (#1097) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 10:55:18 +08:00
欧涛 d882970a32 fix(超算专区): 平台管理员没有前台访问权限 2026-04-29 10:54:49 +08:00
otto eb22a4ebeb Merge pull request 'fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题' (#1096) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 10:52:06 +08:00
欧涛 fcd190bfac fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题 2026-04-29 10:51:16 +08:00
otto 5b05d90522 Merge pull request 'fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题' (#1095) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 10:37:35 +08:00
欧涛 40620e18b5 fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题 2026-04-29 10:36:55 +08:00
otto 7ffbdad4bc Merge pull request 'fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题' (#1094) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-29 10:09:26 +08:00
欧涛 31ad557697 fix(超算专区): 修复审核失败的项目聚合无法重新申请的问题 2026-04-29 10:07:06 +08:00
otto 8f5b4f1882 Merge pull request 'feat(超算专区): 检查当前登录用户在当前专区下权限异常问题' (#1093) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 18:43:02 +08:00
欧涛 820ecc2d61 feat(超算专区): 检查当前登录用户在当前专区下权限异常问题 2026-04-28 18:42:30 +08:00
otto a75d2b55fd Merge pull request 'feat(超算专区): 检查当前登录用户在当前专区下权限异常问题' (#1092) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 18:30:47 +08:00
欧涛 c218b86a39 feat(超算专区): 检查当前登录用户在当前专区下权限异常问题 2026-04-28 18:25:02 +08:00
otto 94fb839f2c Merge pull request 'feat(超算专区): 检查当前登录用户在当前专区下权限异常问题' (#1091) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 18:16:58 +08:00
欧涛 d631abe3a1 feat(超算专区): 检查当前登录用户在当前专区下权限异常问题 2026-04-28 18:16:43 +08:00
otto e2ab5396ea Merge pull request 'feat(超算专区): 存在待审核操作,不允许进行其他操作' (#1090) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 16:43:56 +08:00
欧涛 6982b6451d feat(超算专区): 存在待审核操作,不允许进行其他操作 2026-04-28 16:43:06 +08:00
otto 3b178aa0a6 Merge pull request 'feat(超算专区): 存在待审核操作,不允许进行其他操作' (#1089) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 16:19:47 +08:00
欧涛 b62c85f373 feat(超算专区): 存在待审核操作,不允许进行其他操作 2026-04-28 16:19:35 +08:00
otto 215d7b6914 Merge pull request 'feat(超算专区): 存在待审核操作,不允许进行其他操作' (#1088) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 16:04:12 +08:00
欧涛 6e29169fa4 feat(超算专区): 存在待审核操作,不允许进行其他操作 2026-04-28 16:04:08 +08:00
otto 6c0c459d60 Merge pull request 'feat(超算专区): 存在待审核操作,不允许进行其他操作' (#1087) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 15:45:18 +08:00
欧涛 18d0a9ce3e feat(超算专区): 存在待审核操作,不允许进行其他操作 2026-04-28 15:45:05 +08:00
otto 7930e4a155 Merge pull request 'feat(超算专区): 优化文章删除逻辑' (#1086) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 15:32:07 +08:00
欧涛 77d8a8ae23 feat(超算专区): 存在待审核操作,不允许进行其他操作 2026-04-28 15:32:02 +08:00
欧涛 8dfac68f66 feat(超算专区): 优化文章删除逻辑 2026-04-28 14:38:38 +08:00
otto eef0e81d11 Merge pull request '审核项目聚合时remark未写入数据库' (#1085) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 09:56:18 +08:00
欧涛 054905987f feat(超算专区): 审核项目聚合时remark未写入数据库 2026-04-28 09:56:00 +08:00
otto 91ce2fff08 Merge pull request 'feat(超算专区): 优化全局搜索中搜索逻辑' (#1084) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-28 09:09:22 +08:00
欧涛 08f6aaa414 feat(超算专区): 搜索会员时返回会员用户名,前端需要基于用户名进行跳转 2026-04-28 09:03:30 +08:00
欧涛 7582c5cb11 feat(超算专区): 优化全局搜索中项目搜索逻辑
优化搜索结果中出现匹配度很低的数据
2026-04-28 08:57:12 +08:00
otto f7229b6d42 Merge pull request 'feat(超算专区): 优化全局搜索中项目搜索逻辑' (#1083) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-27 14:28:55 +08:00
欧涛 2fe06ea788 feat(超算专区): 优化全局搜索中项目搜索逻辑 2026-04-27 14:28:24 +08:00
otto ffc5f2e26e Merge pull request 'feat(超算专区): 优化全局搜索中项目搜索逻辑' (#1082) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-27 14:21:13 +08:00
欧涛 01eb5464e4 feat(超算专区): 优化全局搜索中项目搜索逻辑
全局搜索调整为POST请求
2026-04-27 14:19:51 +08:00
otto 13d34fb2e9 Merge pull request 'feat(超算专区): 优化全局搜索中项目搜索逻辑' (#1081) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-27 14:16:37 +08:00
欧涛 6830387bb8 feat(超算专区): 优化全局搜索中项目搜索逻辑
1. 项目搜索时基于Gitlink搜索功能实现
2. 支持项目描述搜索
3. 支持项目标签搜索
2026-04-27 14:16:01 +08:00
otto b429c568f5 Merge pull request 'feat(超算专区): 返回项目聚合列表时需返回项目类型名称' (#1080) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-23 16:53:28 +08:00
欧涛 e2347e6609 feat(超算专区): 返回项目聚合列表时需返回项目类型名称 2026-04-23 16:53:02 +08:00
欧涛 9305ae91c6 Merge branch 'master' of code.gitlink.org.cn:Gitlink/microservices into chaosuan_zone 2026-04-23 16:23:30 +08:00
otto 7dbbbbb090 Merge pull request 'feat(超算专区): 更新Forge删除分支接口' (#1079) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-22 08:51:11 +08:00
欧涛 7f0d3d4dcb feat(超算专区): 更新Forge删除分支接口 2026-04-22 08:47:24 +08:00
otto 6c62ba619b Merge pull request 'feat(超算专区): 更新Forge删除分支接口' (#1078) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-21 17:30:22 +08:00
欧涛 6fb9c13bba feat(超算专区): 更新Forge删除分支接口 2026-04-21 17:29:54 +08:00
otto 900dcdf4d7 Merge pull request 'feat(超算专区): 资源、项目聚合返回审核备注信息' (#1077) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-21 16:34:50 +08:00
欧涛 a6a656820a feat(超算专区): 项目聚合返回审核备注信息 2026-04-21 16:33:35 +08:00
欧涛 0bd824966e feat(超算专区): 资源返回审核备注信息 2026-04-21 16:11:32 +08:00
欧涛 543689fc61 feat(超算专区): 文章表新增备注字段 2026-04-21 16:07:10 +08:00
otto d8c951f9e0 Merge pull request 'feat(超算专区): 文章表新增备注字段' (#1076) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-21 15:03:01 +08:00
欧涛 f6f2ee0f5d feat(超算专区): 文章表新增备注字段 2026-04-21 15:02:37 +08:00
otto 7376dcbef4 Merge pull request 'fix(超算专区): 修复项目聚合被异常删除的问题' (#1075) from otto/microservices:master into master 2026-04-20 15:58:13 +08:00
欧涛 fbb40e3359 fix(超算专区): 修复项目聚合被异常删除的问题 2026-04-20 15:57:22 +08:00
otto 5318bc1a8e Merge pull request '超算专区项目聚合分类优化' (#1073) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-20 11:34:36 +08:00
欧涛 cbb08235f3 Merge branch 'master' of https://gitlink.org.cn/otto/microservices into chaosuan_zone 2026-04-20 11:33:50 +08:00
欧涛 dc6428df41 feat(超算专区): 获取项目聚合分类列表时查询每个分类下关联的项目数量 2026-04-20 11:31:19 +08:00
欧涛 b39ea22a43 Merge remote-tracking branch 'master/master' 2026-04-20 10:34:05 +08:00
欧涛 9404cc38e3 refactor(超算专区): 专区项目聚合类型增加详情JSON字段 2026-04-20 10:33:45 +08:00
otto beb6c6e727 Merge pull request 'refactor(批量聚合项目): 批量聚合项目时不直接抛出异常' (#1072) from otto/microservices:master into master 2026-04-20 09:25:29 +08:00
欧涛 8270bcacd6 refactor(批量聚合项目): 批量聚合项目时不直接抛出异常,通过日志记录警告信息,防止批量聚合项目时已添加项目导致批量聚合失败 2026-04-20 09:17:54 +08:00
欧涛 2897c163f3 refactor(批量聚合项目): 批量聚合项目时不直接抛出异常,通过日志记录警告信息,防止批量聚合项目时已添加项目导致批量聚合失败 2026-04-20 09:15:15 +08:00
otto 16cb2b3477 Merge pull request '补充接口文档中专区搜索分类信息' (#1071) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-17 09:55:54 +08:00
欧涛 354c10f841 feat(全局搜索): 实现超算专区全局搜索功能
补充接口文档中专区搜索分类信息
2026-04-17 09:55:18 +08:00
otto 0cf376a45d Merge pull request '删除无效依赖' (#1070) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-17 09:41:13 +08:00
欧涛 431cd7404d feat(全局搜索): 实现超算专区全局搜索功能
删除无效依赖
2026-04-17 09:40:43 +08:00
otto 181546fab2 Merge pull request 'feat(全局搜索): 实现超算专区全局搜索功能' (#1069) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-17 09:32:10 +08:00
欧涛 3389e839a1 feat(全局搜索): 实现超算专区全局搜索功能
实现搜索结果高亮功能
2026-04-17 09:26:27 +08:00
欧涛 fded90d57c feat(全局搜索): 实现超算专区全局搜索功能
1. 搜索专区项目时返回项目的语言、项目类别、最佳更新时间
2. 搜索专区资源时返回资源的附件数量、下载次数、发布时间
3. 搜索专区新闻时返回新闻的阅读数、发布时间
4. 搜索专区会员时返回会员的会员名称、会员分类名称、会员头像
2026-04-17 08:46:29 +08:00
欧涛 a0384614d3 feat(全局搜索): 实现超算专区全局搜索功能
调整项目、资源、会员搜索逻辑:搜索结果按照全文搜索评分降序排序,同时返回全文搜索评分的分数
2026-04-16 16:26:40 +08:00
欧涛 cd483c065d feat(全局搜索): 实现超算专区全局搜索功能
1. 全局搜索的extendData字段由String类型调整为JSONObject类型,前端使用时更友好
2. 调整文章搜索逻辑:搜索结果按照全文搜索评分降序排序,同时返回全文搜索评分的分数
2026-04-16 15:20:48 +08:00
欧涛 822505b989 feat(全局搜索): 实现超算专区全局搜索功能
调整资源附件匹配逻辑:由FULLTEXT全文检索调整为LIKE检索,保持和JAVA逻辑中的remoteFile接口处理一致
2026-04-16 11:15:06 +08:00
欧涛 9799da2fef feat(全局搜索): 实现超算专区全局搜索功能
问题:使用git关键字搜索gitlink仓库时无法匹配,排查后发现FULLTEXT最小词长限制为4,小于四位的词长不会被匹配,同时对于the、of等停顿词也不会进行匹配
修复方案:补充LIKE作为后备搜索
2026-04-16 11:13:40 +08:00
欧涛 43149d52bf feat(全局搜索): 实现超算专区全局搜索功能
支持专区会员的名称和用户名搜索:会员名称通过FULLTEXT索引进行搜索,用户名使用LIKE条件搜索
2026-04-16 11:08:26 +08:00
欧涛 aff24c83d2 feat(全局搜索): 实现超算专区全局搜索功能
- 实现MySQL全文检索:基于MATCH...AGAINST NATURAL LANGUAGE MODE
   搜索范围覆盖文档标题(name)和摘要(summary)双字段
   结果按全文相关度降序+更新时间降序双重排序
   仅检索已审核通过且非目录类型的文档
- 实现项目全文检索:基于MATCH...AGAINST搜索zone_project.name字段
   仅返回已审核项目(audit_status='1'),按相关度+更新时间排序
- 实现资源全文检索:搜索资源名称(name)+关键词(keywords)复合字段
   创新性支持附件文件名全文匹配(通过EXISTS子查询关联sys_file_info表)
   资源结果附带匹配附件信息(文件ID、文件名、文件大小),存储于extendData
   通过RemoteFileService远程调用获取附件详情,支持大小写不敏感匹配
- 引入Redis缓存策略:第一页搜索结果缓存5分钟
2026-04-15 13:54:04 +08:00
otto 1c6563f915 Merge pull request '前台会员新增项目聚合接口调整为支持批量新增' (#1068) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-14 15:41:06 +08:00
欧涛 4f5f9a283c feat(超算专区): 前台项目聚合功能开发
前台会员新增项目聚合接口调整为支持批量新增
2026-04-14 15:38:46 +08:00
otto a688c9b836 Merge pull request '新增查询专区下我的GitLink项目列表' (#1067) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-14 15:12:04 +08:00
欧涛 77cd5bbc3a feat(超算专区): 前台项目聚合功能开发
新增查询专区下我的GitLink项目列表:支持会员查询我的项目或者我是owner权限的组织下的项目
2026-04-14 15:11:54 +08:00
otto e493757e2b Merge pull request '新增查询专区下我的GitLink项目列表' (#1066) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-14 15:10:34 +08:00
欧涛 e80a0ded7a feat(超算专区): 前台项目聚合功能开发
新增查询专区下我的GitLink项目列表:支持会员查询我的项目或者我是owner权限的组织下的项目
2026-04-14 15:09:43 +08:00
otto c3962a9e3d Merge pull request '删除前台获取项目聚合分类列表接口,共用后台管理接口' (#1065) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-14 08:57:18 +08:00
欧涛 481c3ddfbd feat(超算专区): 前台项目聚合功能开发
删除前台获取项目聚合分类列表接口,共用后台管理接口,取消后台获取分类列表权限限制
2026-04-14 08:55:33 +08:00
otto d30cfd56f2 Merge pull request 'feat(大文件分片上传): 优化断点续传功能' (#1064) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-13 16:31:55 +08:00
欧涛 8c1832780f Merge branch 'chunkedUpload' of code.gitlink.org.cn:Gitlink/microservices into chaosuan_zone 2026-04-13 16:31:31 +08:00
欧涛 8f2ff93dca feat(超算专区): 前台项目聚合功能开发
1. 新增前台查询项目聚合分类接口
2. 调整前台新增/编辑项目逻辑:不允许调整排序字段
2026-04-13 11:18:35 +08:00
欧涛 45bcfeafca Merge branch 'chunkedUpload' of https://gitlink.org.cn/otto/microservices into chaosuan_zone 2026-04-10 16:55:18 +08:00
otto cf03c82425 Merge pull request 'feat(大文件分片上传): 优化断点续传功能' (#1061) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-10 16:37:52 +08:00
欧涛 9f1d89da49 feat(大文件分片上传): 优化断点续传功能
检查秒传时用查询数据库是否存在相同的 fileSignature 且状态为上传中或者合并中的会话,返回该会话的id,前端可以根据该会话id继续完成上传,如果该会话为上传中则返回已上传分片的number,前端可以仅上传未上传的分片,如果为合并中,前端可以调用查询进度接口获取合并进度
2026-04-10 16:36:48 +08:00
欧涛 9301975900 feat(超算专区): 项目聚合审核功能
-新增项目审核状态常量(审核通过/不通过/待审核)
 -ZoneDetail新增projectNeedAudit字段控制专区是否开启项目审核
 -ZoneProject新增auditStatus/auditTime/auditBy审核字段
 -新增ZoneProjectAuditVo审核请求对象
 -实现后台审核接口PUT/project/audit
 -前台新增/编辑项目时,专区开启审核则自动设置待审核状态
 -前台公开项目列表自动过滤仅显示审核通过的项目
 -"我的项目列表"显示所有审核状态的项目
2026-04-10 14:36:24 +08:00
欧涛 33ff6795d1 feat(超算专区): 项目聚合审核功能
-新增项目审核状态常量(审核通过/不通过/待审核)
 -ZoneDetail新增projectNeedAudit字段控制专区是否开启项目审核
 -ZoneProject新增auditStatus/auditTime/auditBy审核字段
 -新增ZoneProjectAuditVo审核请求对象
 -实现后台审核接口PUT/project/audit
 -前台新增/编辑项目时,专区开启审核则自动设置待审核状态
 -前台公开项目列表自动过滤仅显示审核通过的项目
 -"我的项目列表"显示所有审核状态的项目
2026-04-10 14:36:20 +08:00
欧涛 27bd86998c feat(超算专区): 前台项目聚合功能开发
1. 新增查询我的项目列表接口
2. 新增查询我的项目详情接口
3. 新增前台新增项目聚合接口
4. 新增前台编辑项目聚合接口
5. 新增前台删除项目聚合接口
2026-04-10 10:31:26 +08:00
otto 1f4b5c6140 Merge pull request '大文件分片上传功能开发' (#1060) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-09 14:38:23 +08:00
欧涛 56424116ae Merge branch 'chunkedUpload' of code.gitlink.org.cn:Gitlink/microservices into chaosuan_zone 2026-04-09 14:36:24 +08:00
otto 53e5c8c13b Merge pull request 'fix(资源审核): 专区会员发布的待审核资源出现在了资源列表上' (#1054) from otto/microservices:chaosuan_zone into chaosuan_zone 2026-04-08 11:15:51 +08:00
欧涛 ee43cbeaa8 fix(资源审核): 专区会员发布的待审核资源出现在了资源列表上
问题定位:公开接口获取资源列表时未筛选资源审核状态
2026-04-08 11:14:12 +08:00
169 changed files with 10757 additions and 365 deletions

View File

@ -9,6 +9,8 @@ import com.microservices.system.api.factory.RemoteCmsFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户服务
*
@ -158,4 +160,22 @@ public interface RemoteCmsService {
@GetMapping("/doc/getJSONObject/{cmsDocId}")
R<JSONObject> selectCmsDocJSONObjectById(@PathVariable(value = "cmsDocId") Long cmsDocId
, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@PutMapping("/project/changeOrgRole/user/{userId}/operate/{operate}")
R<Boolean> changeUserOrgRole(@PathVariable("userId") Long userId, @PathVariable("operate") int operate
, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 首页文章合并列表接收置顶文章 docId 有序列表返回合并去重后的结果
* Zone 服务 /open/getTopDoc 改造后调用
*
* @param zoneId 专区ID
* @param topDocIds 置顶文章 docId 有序列表
* @param source 来源
* @return 合并后的首页文章 JSONObject 列表
*/
@PostMapping("/doc/homePageDocMergeList/{zoneId}")
R<List<JSONObject>> selectHomePageDocMergeList(@PathVariable("zoneId") Long zoneId,
@RequestBody List<Long> topDocIds,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -85,7 +85,7 @@ public interface RemoteDeptService {
* 根据组织id删除部门
*
* @param deptId 部门id
* @param source 请求来源
* @param source 请求来源
* @return 是否删除成功
*/
@DeleteMapping("/dept/deleteDeptByDeptId")
@ -105,7 +105,7 @@ public interface RemoteDeptService {
* 通过用户名查询用户下组织及组织对应角色列表
*
* @param deptId 组织ID
* @param source 请求来源
* @param source 请求来源
* @return 用户下组织及组织对应角色列表
*/
@GetMapping("/SysUserDeptRole/userByDeptId/{deptId}/list")
@ -115,7 +115,7 @@ public interface RemoteDeptService {
/**
* 查询组织编号下拥有传入角色权限字符串的用户名
*
* @param deptId 组织编号
* @param deptId 组织编号
* @param roleKeys 角色权限字符串
* @return 组织下拥有传入角色权限字符串的用户名列表
*/
@ -126,7 +126,7 @@ public interface RemoteDeptService {
* 检查用户是否在指定部门下具有指定角色
*
* @param currentDeptId 组织编号
* @param roleKey 角色权限字符串
* @param roleKey 角色权限字符串
* @return 组织下拥有传入角色权限字符串的用户名列表
*/
@GetMapping("/SysUserDeptRole/checkUserInDeptHasRole")
@ -143,6 +143,17 @@ public interface RemoteDeptService {
@GetMapping("/SysUserDeptRole/checkUserInDeptHasPerms")
R<Boolean> checkUserInDeptHasPerms(@RequestParam("currentDeptId") Long currentDeptId, @RequestParam("userId") Long userId, @RequestParam("perms") String perms, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 检查用户是否在指定部门或指定部门的父级部门下具有指定菜单权限
*
* @param deptId 指定组织id
* @param userId 用户id
* @param perms 菜单权限
* @return 用户是否在指定部门下具有菜单权限 是true 否false
*/
@GetMapping("/SysUserDeptRole/checkUserInDeptOrParentDeptHasPerms")
R<Boolean> checkUserInDeptOrParentDeptHasPerms(@RequestParam("deptId") Long deptId, @RequestParam("userId") Long userId, @RequestParam("perms") String perms, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 检查用户是否在指定部门下
*
@ -152,4 +163,7 @@ public interface RemoteDeptService {
*/
@GetMapping("/SysUserDeptRole/checkUserInDept")
R<Boolean> checkUserInDept(@RequestParam("currentDeptId") Long currentDeptId, @RequestParam("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@GetMapping("/SysUserDeptRole/getSysUserDeptRoleByDeptIdAndUserId")
R<SysUserDeptRole> getSysUserDeptRoleByDeptIdAndUserId(@RequestParam("deptId") Long deptId, @RequestParam("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -57,6 +57,15 @@ public interface RemoteUserService {
@GetMapping("/user/getSysUserByUserId/{userId}")
public R<SysUser> getSysUserByUserId(@PathVariable("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 通过用户ID数组批量获取用户信息POST方法解决URL长度限制
*
* @param userIds 用户ID数组
* @param source 请求来源
* @return 结果
*/
@PostMapping("/user/getSysUserListByUserIds")
R<List<SysUser>> postSysUserListByUserIds(@RequestBody Long[] userIds, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 组织下新增用户身份
@ -64,7 +73,7 @@ public interface RemoteUserService {
* @param deptId
* @param userId
* @param roleKey
* @param source 请求来源
* @param source 请求来源
* @return 结果
*/
@PostMapping("/SysUserDeptRole/dept/{deptId}/user/{userId}/role/{roleKey}")
@ -74,14 +83,13 @@ public interface RemoteUserService {
, @PathVariable("roleKey") String roleKey
, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 组织下删除用户身份
*
* @param deptId
* @param userId
* @param roleKey
* @param source 请求来源
* @param source 请求来源
* @return 结果
*/
@DeleteMapping("/SysUserDeptRole/dept/{deptId}/user/{userId}/role/{roleKey}")
@ -91,18 +99,16 @@ public interface RemoteUserService {
@PathVariable("roleKey") String roleKey,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 注册用户信息
*
* @param sysUser 用户信息
* @param source 请求来源
* @param source 请求来源
* @return 结果
*/
@PostMapping("/user/register")
public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 通过GitLinkToken获取本系统Token
*
@ -123,7 +129,6 @@ public interface RemoteUserService {
@GetMapping("/SysUserDeptRole/userByUserName/{userName}/list")
public R<List<SysUserDeptRole>> getSysUserDeptRoleListByUserName(@PathVariable("userName") String userName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 通过用户名查询用户下第一个身份
*
@ -195,7 +200,7 @@ public interface RemoteUserService {
/**
* 通过GitLink用户Id创建用户
*
* @param gitLinkUserId GitLink用户Id
* @param gitLinkUserId GitLinkUserId
* @param source 请求来源
* @return 系统用户
*/
@ -206,7 +211,7 @@ public interface RemoteUserService {
* 根据角色Key获取菜单权限集合
*
* @param roleKey 角色Key
* @param source 请求来源
* @param source 请求来源
* @return 菜单权限集合
*/
@GetMapping("/menu/roleMenuPermissionByRoleKey/{roleKey}")

View File

@ -9,6 +9,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 内容管理服务降级处理
*
@ -83,6 +85,16 @@ public class RemoteCmsFallbackFactory implements FallbackFactory<RemoteCmsServic
public R<JSONObject> selectCmsDocJSONObjectById(Long cmsDocId, String source) {
return R.fail("获取文章JSON对象失败:" + throwable.getMessage());
}
@Override
public R<Boolean> changeUserOrgRole(Long userId, int operate, String source) {
return R.fail("更新组织权限失败:" + throwable.getMessage());
}
@Override
public R<List<JSONObject>> selectHomePageDocMergeList(Long zoneId, List<Long> topDocIds, String source) {
return R.fail("获取首页文章合并列表失败:" + throwable.getMessage());
}
};
}
}

View File

@ -83,10 +83,20 @@ public class RemoteDeptFallbackFactory implements FallbackFactory<RemoteDeptServ
return R.fail("检查用户是否在指定部门下具有指定菜单权限失败:" + throwable.getMessage());
}
@Override
public R<Boolean> checkUserInDeptOrParentDeptHasPerms(Long deptId, Long userId, String perms, String source) {
return R.fail("检查用户是否在指定部门或指定部门的父级部门下具有指定菜单权限失败:" + throwable.getMessage());
}
@Override
public R<Boolean> checkUserInDept(Long currentDeptId, Long userId, String source) {
return R.fail("检查用户是否在指定部门下失败:" + throwable.getMessage());
}
@Override
public R<SysUserDeptRole> getSysUserDeptRoleByDeptIdAndUserId(Long deptId, Long userId, String source) {
return R.fail("获取用户在指定部门下的角色失败:" + throwable.getMessage());
}
};
}
}

View File

@ -52,6 +52,12 @@ public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserServ
return R.fail("获取用户失败:" + throwable.getMessage());
}
@Override
public R<List<SysUser>> postSysUserListByUserIds(Long[] userIds, String source) {
return R.fail("批量获取用户失败:" + throwable.getMessage());
}
@Override
public R<Boolean> addUserIdentify(Long deptId, Long userId, String roleKey, String source) {
return R.fail("组织下新增用户身份失败:" + throwable.getMessage());

View File

@ -80,6 +80,10 @@ public class CacheConstants {
* 通过组织Id获取专区详情JSON对象
*/
public final static String ZONE_JSON_OF_DEPT_ID_KEY = "zoneJSONOfDeptId:";
/**
* 积分发放部门
*/
public final static String ZONE_POINTS_DEPT_ID_KEY = "zonePointsDeptId";
/**
* 内容管理同步缓存前缀
*/
@ -245,6 +249,18 @@ public class CacheConstants {
return ZONE_PROJECT_SORT_BY_SCORE + "*:" + zoneId + ":*";
}
/**
* 专区统计数据缓存前缀
*/
private final static String ZONE_STATISTICS_KEY = "zone:statistics:";
/**
* 获取专区统计数据缓存key
*/
public static String getZoneStatisticsKey(Long zoneId) {
return ZONE_STATISTICS_KEY + zoneId;
}
/**
* 文件打包是否完成0代表未完成1代表已完成-1代表打包失败需要重新打包
*/

View File

@ -5,8 +5,7 @@ package com.microservices.common.core.constant;
*
* @author microservices
*/
public class UserConstants
{
public class UserConstants {
/**
* 平台内系统用户的唯一标志
*/
@ -28,55 +27,89 @@ public class UserConstants
*/
public static final String DEL_FLAG_FALSE = "0";
/** 正常状态 */
/**
* 正常状态
*/
public static final String NORMAL = "0";
/** 异常状态 */
/**
* 异常状态
*/
public static final String EXCEPTION = "1";
/** 用户封禁状态 */
/**
* 用户封禁状态
*/
public static final String USER_DISABLE = "1";
/** 角色封禁状态 */
/**
* 角色封禁状态
*/
public static final String ROLE_DISABLE = "1";
/** 部门正常状态 */
/**
* 部门正常状态
*/
public static final String DEPT_NORMAL = "0";
/** 部门停用状态 */
/**
* 部门停用状态
*/
public static final String DEPT_DISABLE = "1";
/** 字典正常状态 */
/**
* 字典正常状态
*/
public static final String DICT_NORMAL = "0";
/** 是否为系统默认(是) */
/**
* 是否为系统默认
*/
public static final String YES = "Y";
/** 是否菜单外链(是) */
/**
* 是否菜单外链
*/
public static final String YES_FRAME = "0";
/** 是否菜单外链(否) */
/**
* 是否菜单外链
*/
public static final String NO_FRAME = "1";
/** 菜单类型(目录) */
/**
* 菜单类型目录
*/
public static final String TYPE_DIR = "M";
/** 菜单类型(菜单) */
/**
* 菜单类型菜单
*/
public static final String TYPE_MENU = "C";
/** 菜单类型(按钮) */
/**
* 菜单类型按钮
*/
public static final String TYPE_BUTTON = "F";
/** Layout组件标识 */
/**
* Layout组件标识
*/
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
/**
* ParentView组件标识
*/
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
/**
* InnerLink组件标识
*/
public final static String INNER_LINK = "InnerLink";
/** 校验是否唯一的返回标识 */
/**
* 校验是否唯一的返回标识
*/
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
@ -122,4 +155,28 @@ public class UserConstants
* 项目管理-知识库管理权限
*/
public static final String PMS_PERMS_DOCS_MANAGE = "pms:docs:manage";
/**
* 文章管理-文章删除权限
*/
public static final String CMS_PERMS_DOCS_DELETE = "cms:doc:remove";
/**
* 文章管理-新增文章权限
*/
public static final String CMS_PERMS_DOCS_ADD = "cms:doc:add";
/**
* 文章管理-编辑文章权限
*/
public static final String CMS_PERMS_DOCS_EDIT = "cms:doc:edit";
/**
* 文章管理-审核文章权限
*/
public static final String CMS_PERMS_DOCS_AUDIT = "cms:doc:audit";
/**
* 专区管理-审核项目权限
*/
public static final String ZONE_PERMS_PROJECT_AUDIT = "zone:project:audit";
/**
* 专区管理-审核资源权限
*/
public static final String ZONE_PERMS_RESOURCE_AUDIT = "zone:resource:audit";
}

View File

@ -8,7 +8,7 @@ package com.microservices.common.core.enums;
public enum DocAndZoneOperation
{
OPERATION_CREATE("创建", "create"), OPERATION_UPDATE("更新", "update"),
OPERATION_DELETE("删除", "delete");
OPERATION_DELETE("删除", "delete"), OPERATION_NON_REPO_UPDATE("非仓库更新", "nonRepoUpdate");
private final String operationName;
private final String operationCode;

View File

@ -211,6 +211,17 @@ public class HttpAPIService {
return doRequest(httpDelete, headers);
}
/**
* 不带Header的get请求
*
* @param url
* @return
* @throws Exception
*/
public JSONObject doGet(String url) throws Exception {
return doGet(url, null);
}
/**
* 带Header的get请求
*

View File

@ -115,8 +115,7 @@ public class AuthLogic {
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission)
{
public boolean hasPermi(String permission) {
return hasPermi(tokenService.getMenuPermissions(), permission);
}
@ -126,10 +125,8 @@ public class AuthLogic {
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public void checkPermi(String permission)
{
if (!hasPermi(tokenService.getMenuPermissions(), permission))
{
public void checkPermi(String permission) {
if (!hasPermi(tokenService.getMenuPermissions(), permission)) {
throw new NotPermissionException(permission);
}
}
@ -153,13 +150,10 @@ public class AuthLogic {
*
* @param permissions 权限列表
*/
public void checkPermiAnd(String... permissions)
{
public void checkPermiAnd(String... permissions) {
Set<String> permissionList = tokenService.getMenuPermissions();
for (String permission : permissions)
{
if (!hasPermi(permissionList, permission))
{
for (String permission : permissions) {
if (!hasPermi(permissionList, permission)) {
throw new NotPermissionException(permission);
}
}
@ -170,13 +164,10 @@ public class AuthLogic {
*
* @param permissions 权限码数组
*/
public void checkPermiOr(String... permissions)
{
public void checkPermiOr(String... permissions) {
Set<String> permissionList = tokenService.getMenuPermissions();
for (String permission : permissions)
{
if (hasPermi(permissionList, permission))
{
for (String permission : permissions) {
if (hasPermi(permissionList, permission)) {
return;
}
}
@ -191,8 +182,7 @@ public class AuthLogic {
* @param role 角色标识
* @return 用户是否具备某角色
*/
public boolean hasRole(String role)
{
public boolean hasRole(String role) {
return hasRole(tokenService.getRolePermissions(), role);
}
@ -225,13 +215,10 @@ public class AuthLogic {
*
* @param roles 角色标识数组
*/
public void checkRoleAnd(String... roles)
{
public void checkRoleAnd(String... roles) {
Set<String> roleList = tokenService.getRolePermissions();
for (String role : roles)
{
if (!hasRole(roleList, role))
{
for (String role : roles) {
if (!hasRole(roleList, role)) {
throw new NotRoleException(role);
}
}
@ -242,18 +229,14 @@ public class AuthLogic {
*
* @param roles 角色标识数组
*/
public void checkRoleOr(String... roles)
{
public void checkRoleOr(String... roles) {
Set<String> roleList = tokenService.getRolePermissions();
for (String role : roles)
{
if (hasRole(roleList, role))
{
for (String role : roles) {
if (hasRole(roleList, role)) {
return;
}
}
if (roles.length > 0)
{
if (roles.length > 0) {
throw new NotRoleException(roles);
}
}
@ -323,6 +306,9 @@ public class AuthLogic {
public boolean hasUserIdentity(Long deptId, String roleKey) {
LoginUser loginUser = getLoginUser();
List<SysUserDeptRole> sysUserDeptRoleList = tokenService.getAllUserIdentity(loginUser.getUsername());
if (sysUserDeptRoleList == null) {
return false;
}
return sysUserDeptRoleList.stream()
.anyMatch(x -> {
if (x.getDeptId() != null && x.getSysRole() != null && x.getDeptId().equals(deptId)) {

View File

@ -11,6 +11,7 @@ import com.microservices.common.core.utils.uuid.IdUtils;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.interceptor.HeaderInterceptor;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteDeptService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.domain.SysRole;
import com.microservices.system.api.domain.SysUser;
@ -38,6 +39,8 @@ public class TokenService {
private RedisService redisService;
@Autowired
private RemoteUserService remoteUserService;
@Autowired
private RemoteDeptService remoteDeptService;
protected static final long MILLIS_SECOND = 1000;
@ -329,4 +332,16 @@ public class TokenService {
}
return loginUser.getSysUser();
}
public Boolean hasDeptPerms(Long deptId, String perms) {
LoginUser loginUser = SecurityUtils.getLoginUser();
Boolean hasPerms = FeignUtils.getReturnData(
remoteDeptService.checkUserInDeptOrParentDeptHasPerms(deptId, loginUser.getUserId(), perms, SecurityConstants.INNER)
);
if (hasPerms == null) {
hasPerms = false;
}
return hasPerms;
}
}

View File

@ -3,6 +3,7 @@ package com.microservices.cms.doc.controller;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.cms.doc.domain.*;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.common.core.constant.UserConstants;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
@ -17,6 +18,7 @@ import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
@ -106,23 +108,13 @@ public class CmsDocController extends BaseController {
@ApiImplicitParam(name = "pageNum", value = "当前记录起始索引", paramType = "query", dataType = "Integer"),
@ApiImplicitParam(name = "pageSize", value = "每页显示记录数", paramType = "query", dataType = "Integer"),
})
public TableDataInfo docListByZone(
public GenericsTableDataInfo<CmsDocAllDto> docListByZone(
@ApiParam(name = "zoneId", value = "专区Id") @PathVariable("zoneId") Long zoneId
, CmsDocSearchVo cmsDocSearchVo) {
Long deptId = cmsDocService.getDeptIdByZoneId(zoneId);
cmsDocSearchVo.setIsOpen(false);
cmsDocSearchVo.setIsOpen(true);
startPage();
List<CmsDocAllDto> list = cmsDocService.selectCmsDocListByDept(deptId, cmsDocSearchVo);
list.forEach(x -> {
String docDetailUrl = cmsDocService.getDocDetailUrl(x.getDeptId(), x.getId());
x.setDocDetailUrl(docDetailUrl);
Boolean editable = cmsDocService.isCmsDocEditable(x);
x.setEditable(editable);
});
int count = cmsDocService.selectCmsDocCountByDept(deptId, cmsDocSearchVo);
return getDataTable(list, count);
return cmsDocService.selectAllDocList(deptId, cmsDocSearchVo);
}
/**
@ -164,7 +156,7 @@ public class CmsDocController extends BaseController {
/**
* 新增文章信息
*/
@RequiresPermissions("cms:doc:add")
@RequiresPermissions(UserConstants.CMS_PERMS_DOCS_ADD)
@Log(title = "文章信息", businessType = BusinessType.INSERT)
@PostMapping("/dir/{dirId}")
@ApiOperation("在栏目下创建文章")
@ -201,7 +193,7 @@ public class CmsDocController extends BaseController {
/**
* 删除文章
*/
@RequiresPermissions("cms:doc:remove")
@RequiresPermissions(UserConstants.CMS_PERMS_DOCS_DELETE)
@Log(title = "文章信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除文章/栏目")
@ -212,13 +204,13 @@ public class CmsDocController extends BaseController {
/**
* 修改文章信息
*/
@RequiresPermissions("cms:doc:edit")
@RequiresPermissions(UserConstants.CMS_PERMS_DOCS_EDIT)
@Log(title = "文章信息", businessType = BusinessType.UPDATE)
@PutMapping("/{id}")
@ApiOperation("更新文章")
public AjaxResult edit(@ApiParam(name = "id", value = "文章Id") @PathVariable Long id,
@ApiParam(name = "cmsDoc", value = "文章实体对象") @RequestBody CmsDocDto cmsDocDto) {
return toAjax(cmsDocService.updateCmsDoc(id, cmsDocDto));
return success(cmsDocService.updateCmsDoc(id, cmsDocDto));
}
/**
@ -237,12 +229,12 @@ public class CmsDocController extends BaseController {
/**
* 审核文章信息
*/
@RequiresPermissions("cms:doc:audit")
@RequiresPermissions(UserConstants.CMS_PERMS_DOCS_AUDIT)
@Log(title = "文章信息", businessType = BusinessType.UPDATE)
@PutMapping("/{id}/audit")
@ApiOperation("审核文章")
public AjaxResult audit(@ApiParam(name = "id", value = "文章Id") @PathVariable Long id,
@ApiParam(name = "cmsDocAuditVo", value = "文章审核对象") @RequestBody CmsDocAuditVo cmsDocAuditVo) {
@ApiParam(name = "cmsDocAuditVo", value = "文章审核对象") @RequestBody CmsDocAuditVo cmsDocAuditVo) {
return toAjax(cmsDocService.auditCmsDoc(id, cmsDocAuditVo));
}
@ -315,6 +307,24 @@ public class CmsDocController extends BaseController {
return R.ok(cmsDocJSONObject);
}
/**
* 内部接口首页文章合并列表接收置顶文章 docId 有序列表返回合并去重后的结果
* Zone 服务 Feign 调用用于 /open/getTopDoc 改造
*/
@InnerAuth
@PostMapping("/homePageDocMergeList/{zoneId}")
@ApiOperation(value = "首页文章合并列表", hidden = true)
public R<List<JSONObject>> selectHomePageDocMergeList(
@PathVariable("zoneId") Long zoneId,
@RequestBody List<Long> topDocIds) {
List<CmsDocBaseDto> list = cmsDocService.selectHomePageDocMergeList(zoneId, topDocIds);
List<JSONObject> result = new ArrayList<>();
for (CmsDocBaseDto dto : list) {
result.add(JSONObject.from(dto));
}
return R.ok(result);
}
/**
* 通过组织Id获取专区下文章阅读数量
*

View File

@ -5,12 +5,17 @@ import com.microservices.cms.doc.domain.*;
import com.microservices.cms.doc.service.ICmsDocCommentService;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.cms.doc.service.ICmsWechatService;
import com.microservices.cms.search.dto.DocSearchResult;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.core.web.page.TableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.cms.search.dto.DocSearchRequest;
import com.microservices.cms.search.dto.DocSearchResponse;
import com.microservices.cms.search.service.IGlobalSearchService;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -35,6 +40,22 @@ public class OpenController extends BaseController {
@Autowired
private ICmsWechatService cmsWechatService;
@Autowired
private IGlobalSearchService globalSearchService;
/**
* 文章搜索接口
*
* @return 搜索结果
*/
@ApiOperation("文章搜索")
@PostMapping("/search")
public GenericsTableDataInfo<DocSearchResult> docSearch(@RequestBody DocSearchRequest docSearchRequest) {
logger.info("文章搜索请求: keyword={}", docSearchRequest.getKeyword());
DocSearchResponse response = globalSearchService.docSearch(docSearchRequest);
return new GenericsTableDataInfo<>(response.getResults(), response.getTotal());
}
/**
* 查询栏目列表
*/
@ -100,6 +121,19 @@ public class OpenController extends BaseController {
return getAllGenericsDataTableToPage(list);
}
/**
* 查询文章列表不区分栏目
*/
@GetMapping("/zone/{zoneId}/allDocList")
@ApiOperation("查询文章列表(不区分栏目)")
public GenericsTableDataInfo<CmsDocAllDto> allDocList(@ApiParam(name = "zoneId", value = "专区Id") @PathVariable("zoneId") Long zoneId,
CmsDocSearchVo cmsDocSearchVo) {
Long deptId = cmsDocService.getDeptIdByZoneId(zoneId);
cmsDocSearchVo.setIsOpen(true);
startPage();
return cmsDocService.selectAllDocList(deptId, cmsDocSearchVo);
}
/**
* 查询首页文章列表
*/
@ -175,9 +209,9 @@ public class OpenController extends BaseController {
@Log(title = "同步文章数据", businessType = BusinessType.UPDATE)
public AjaxResult syncDocData(
@ApiParam(name = "deptId", value = "组织Id", required = true) @PathVariable("deptId") Long deptId
,@RequestBody() String bodyStr
, @RequestBody() String bodyStr
, HttpServletRequest request) {
cmsDocService.syncDocData(deptId,bodyStr);
cmsDocService.syncDocData(deptId, bodyStr);
return AjaxResult.success("同步消息发送成功");
}

View File

@ -3,6 +3,7 @@ package com.microservices.cms.doc.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.microservices.cms.utils.CmsConstants;
import com.microservices.common.core.annotation.Excel;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.common.core.web.domain.BaseSortEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -36,19 +37,27 @@ public class CmsDoc extends BaseSortEntity {
@Excel(name = "文件名称")
private String fileName;
/** 文件路径 */
/**
* 文件路径
*/
@Excel(name = "文件路径")
private String filePath;
/** 文件SHA */
/**
* 文件SHA
*/
@Excel(name = "文件SHA")
private String fileSha;
/** 文件大小 */
/**
* 文件大小
*/
@Excel(name = "文件大小")
private Long fileSize;
/** 文件URL */
/**
* 文件URL
*/
@Excel(name = "文件URL")
private String fileUrl;
@ -162,6 +171,11 @@ public class CmsDoc extends BaseSortEntity {
*/
private String keywords;
/**
* 备注
*/
private String remark;
public CmsDir copyToCmsDir() {
CmsDir cmsDir = new CmsDir();
cmsDir.setId(id);
@ -210,30 +224,12 @@ public class CmsDoc extends BaseSortEntity {
}
private CmsDocBaseDto getCmsDocBaseDto(CmsDocBaseDto cmsDocDto) {
cmsDocDto.setId(id);
cmsDocDto.setName(name);
cmsDocDto.setHeadImg(headImg);
cmsDocDto.setIsHomepage(isHomepage);
cmsDocDto.setSummary(summary);
cmsDocDto.setEditorType(editorType);
cmsDocDto.setDeptId(deptId);
BeanUtils.copyProperties(this, cmsDocDto);
if (visits == null) {
cmsDocDto.setVisits(0L);
} else {
cmsDocDto.setVisits(visits);
}
cmsDocDto.setPublishTime(publishTime);
cmsDocDto.setPublishUser(publishUser);
cmsDocDto.setPublishUserUrl(publishUserUrl);
cmsDocDto.setPublishUserImage(publishUserImage);
cmsDocDto.setPublishUserNickname(publishUserNickname);
cmsDocDto.setAuditStatus(auditStatus);
cmsDocDto.setAuditType(auditType);
cmsDocDto.setCreateBy(createBy);
cmsDocDto.setKeywords(keywords);
return cmsDocDto;
}

View File

@ -20,4 +20,10 @@ public class CmsDocAuditVo {
*/
@ApiModelProperty(value = "是否重新编辑: 0不允许重新编辑 1允许重新编辑")
private Integer reedit;
/**
* 备注
*/
@ApiModelProperty(value = "审核备注")
private String remark;
}

View File

@ -101,6 +101,10 @@ public class CmsDocBaseDto {
*/
@ApiModelProperty("关键词")
private String keywords;
/**
* 备注
*/
private String remark;
public void setPublishTime(Date publishTime) {
if (publishTime != null) {

View File

@ -83,4 +83,10 @@ public class CmsDocSearchVo {
*/
@ApiModelProperty("关键词")
private String keywords;
/**
* 排除文章Id
*/
@ApiModelProperty(value = "排除文章Id", hidden = true)
private Long excludeId;
}

View File

@ -0,0 +1,23 @@
package com.microservices.cms.doc.domain;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* CmsDoc with full-text search score
* 用于全局搜索结果包含全文搜索相关度评分
*
* @author otto
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CmsDocWithScore extends CmsDoc implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 全文搜索相关度评分来自 MySQL MATCH...AGAINST
*/
private Double searchScore;
}

View File

@ -2,6 +2,7 @@ package com.microservices.cms.doc.mapper;
import com.microservices.cms.doc.domain.CmsDoc;
import com.microservices.cms.doc.domain.CmsDocSearchVo;
import com.microservices.cms.doc.domain.CmsDocWithScore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -23,6 +24,14 @@ public interface CmsDocMapper {
*/
public CmsDoc selectCmsDocById(Long id);
/**
* 批量查询文章信息
*
* @param ids 文章ID列表
* @return 文章信息列表
*/
List<CmsDoc> selectCmsDocByIds(@Param("ids") List<Long> ids);
/**
* 查询文章信息列表
*
@ -82,6 +91,11 @@ public interface CmsDocMapper {
CmsDoc selectCmsDocByFilePathAndDeptId(@Param("filePath") String filePath, @Param("deptId") Long deptId);
/**
* 按文章名+deptId查询忽略dirName用于Webhook防重判断
*/
CmsDoc selectCmsDocByNameAndDeptIdIgnoreDirName(@Param("name") String name, @Param("deptId") Long deptId);
List<CmsDoc> selectHotCmsDocList(CmsDoc cmsDocInput);
List<CmsDoc> selectCmsDocDirByDeptIdAndInName(@Param("deptId") Long deptId, @Param("cmsDirNameList") List<String> cmsDirNameList);
@ -192,4 +206,41 @@ public interface CmsDocMapper {
List<CmsDoc> selectCmsDocAndDirListByDept(Long deptId);
void updateCmsDocShaByPathAndDeptId(@Param("sha") String sha, @Param("filePath") String filePath, @Param("deptId") Long deptId);
/**
* 全局搜索 - 全文索引查询
*
* @param keyword 搜索关键词
* @param deptId 组织ID可选
* @param offset 偏移量
* @param pageSize 每页数量
* @return 搜索结果列表
*/
List<CmsDoc> globalSearch(@Param("keyword") String keyword, @Param("deptId") Long deptId,
@Param("offset") int offset, @Param("pageSize") int pageSize);
/**
* 全局搜索结果计数
*
* @param keyword 搜索关键词
* @param deptId 组织ID可选
* @return 结果数量
*/
Long globalSearchCount(@Param("keyword") String keyword, @Param("deptId") Long deptId);
/**
* 全局搜索 - 全文索引查询含评分
*
* @param keyword 搜索关键词
* @param deptId 组织ID可选
* @param offset 偏移量
* @param pageSize 每页数量
* @return 搜索结果列表含评分
*/
List<CmsDocWithScore> globalSearchWithScore(
@Param("keyword") String keyword,
@Param("deptId") Long deptId,
@Param("offset") int offset,
@Param("pageSize") int pageSize
);
}

View File

@ -99,4 +99,38 @@ public interface ICmsAsyncService {
* @param cmsDoc 文章
*/
void pushDocDataSearchEngine(CmsDoc cmsDoc);
void clearTmpDoc(CmsProject cmsProject,List<CmsDoc> tmpDocList);
/**
* 异步增加文章发布积分
*
* @param deptId 组织ID
* @param createBy 创建人用户名
* @param docId 文章ID
* @param subjectDesc 文章标题
*/
void addPublishDocPointsAsync(Long deptId, String createBy, Long docId, String subjectDesc);
/**
* 异步增加文章浏览积分每满100次+2上限50
*
* @param deptId 组织ID
* @param createBy 创建人用户名
* @param docId 文章ID
* @param docName 文章名称
* @param totalVisits 累计阅读次数
* @param pointsTriggerCount 当前第几次增加积分
*/
void addVisitsDocPointsAsync(Long deptId, String createBy, Long docId, String docName, Long totalVisits);
/**
* 异步增加文章被设为精选的积分
*
* @param deptId 组织ID
* @param createBy 创建人用户名
* @param docId 文章ID
* @param subjectDesc 文章标题
*/
void addFeaturedDocPointsAsync(Long deptId, String createBy, Long docId, String subjectDesc);
}

View File

@ -5,6 +5,7 @@ import com.microservices.cms.doc.domain.*;
import com.microservices.cms.doc.domain.gitlink.EntryDto;
import com.microservices.cms.doc.domain.gitlink.FileEntryDto;
import com.microservices.cms.project.domain.CmsProject;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@ -56,9 +57,9 @@ public interface ICmsDocService {
*
* @param id
* @param cmsDocDto 文章信息
* @return 结果
* @return 结果 文章id
*/
public boolean updateCmsDoc(Long id, CmsDocDto cmsDocDto);
public Long updateCmsDoc(Long id, CmsDocDto cmsDocDto);
/**
* 批量删除文章信息
@ -144,6 +145,15 @@ public interface ICmsDocService {
*/
List<CmsDocBaseDto> selectHomePageDocList(Long deptId);
/**
* 查询首页文章合并列表积分置顶文章 + 首页推荐文章自动去重置顶在前
*
* @param zoneId 专区ID
* @param topDocIds 置顶文章 docId 有序列表前端按此顺序排列
* @return 合并后的首页文章列表isTop=true 标识置顶文章
*/
List<CmsDocBaseDto> selectHomePageDocMergeList(Long zoneId, List<Long> topDocIds);
/**
* 初始化栏目
*
@ -198,6 +208,8 @@ public interface ICmsDocService {
*/
List<CmsDocAllDto> selectCmsDocListByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo);
GenericsTableDataInfo<CmsDocAllDto> selectAllDocList(Long deptId, CmsDocSearchVo cmsDocSearchVo);
/**
* 查询专区下文章数量
*
@ -294,4 +306,6 @@ public interface ICmsDocService {
List<CmsDoc> selectCmsDocAndDirListByDept(Long deptId);
void updateCmsDocShaByPathAndDeptId(String sha, String filePath, Long deptId);
List<CmsDoc> getTmpDocList(CmsDoc cmsDoc);
}

View File

@ -20,6 +20,8 @@ import com.microservices.cms.utils.CustomExecutorFactory;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.constant.Constants;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.enums.DocAndZoneOperation;
import com.microservices.common.core.enums.DocAuditStatus;
import com.microservices.common.core.enums.SystemRole;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
@ -33,6 +35,7 @@ import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteDeptService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.RemoteZoneService;
import com.microservices.system.api.utils.FeignUtils;
import org.apache.http.client.config.RequestConfig;
@ -95,6 +98,8 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
@Autowired
private RemoteZoneService remoteZoneService;
@Autowired
private RemoteUserService remoteUserService;
@Autowired
private RequestConfig config;
/**
@ -251,7 +256,16 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
}
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocByNameAndDeptId(CmsUtils.getFileNameNoSuffix(entry.getName()), cmsProject.getDeptId(), dirName);
if (cmsDoc == null) {
syncCreateDoc(entry, dirName, cmsProject.getDeptId(), fileEntry.getLast_commit());
// 防重按文章名+deptId查一次排除正在移动栏目的已有记录
String docName = CmsUtils.getFileNameNoSuffix(entry.getName());
CmsDoc existingDoc = cmsDocMapper.selectCmsDocByNameAndDeptIdIgnoreDirName(docName, cmsProject.getDeptId());
if (existingDoc != null) {
existingDoc.setDirName(dirName);
existingDoc.setFilePath(dirName + "/" + existingDoc.getFileName());
cmsDocMapper.updateCmsDoc(existingDoc);
} else {
syncCreateDoc(entry, dirName, cmsProject.getDeptId(), fileEntry.getLast_commit());
}
}
}
} catch (Exception e) {
@ -618,11 +632,12 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
100, "..."), tmpBranchName);
} catch (ServiceException e) {
//忽略重复创建PR异常
if (!"请求GitLink失败(合并请求已存在)".equals(e.getMessage())) {
if (!e.getMessage().contains("合并请求已存在")) {
throw new ServiceException(e.getMessage());
}
}
cmsDocService.updateCmsCommonDatabase(cmsDoc, tmpBranchName);
}
@ -723,8 +738,8 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
if (isChangeName || isChangeDir) {
oldCmsDoc = renameAndChangeDirCmsDoc(oldCmsDoc, cmsProject, newName, newDirName, branchName);
//todo 此处是否需要更新数据库待讨论
//updateCmsCommonDatabase(oldCmsDoc);
// 立即更新数据库中的dirName/filePath避免GitLink Webhook回调时查不到记录导致重复INSERT
cmsDocMapper.updateCmsDoc(oldCmsDoc);
}
boolean isChangeContent = false;
@ -1003,6 +1018,23 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
}
}
@Override
public void clearTmpDoc(CmsProject cmsProject, List<CmsDoc> tmpDocList) {
CustomExecutorFactory.threadPoolExecutor.execute(() -> {
for (CmsDoc tmpDoc : tmpDocList) {
//拒绝GitLink合并请求
String gitLinkPullRequestNumber = tmpDoc.getAuditPrNumber().split("-")[0];
gitLinkRequestHelper.doPost(
CmsGitLinkRequestUrl.REFUSE_MERGE_PULL_REQUEST(cmsProject, gitLinkPullRequestNumber), null);
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, tmpDoc.getAuditTmpBranchName()), null);
// 所有删除操作调整为将审核状态设置为已拒绝
tmpDoc.setAuditStatus(DocAuditStatus.AUDIT_FAIL.getAuditCode());
tmpDoc.setRemark("管理员删除");
cmsDocMapper.updateCmsDoc(tmpDoc);
}
});
}
private void moveCmsDocToNewCmsDir(CmsProject cmsProject, String oldDirName, String newDirName, String message) {
CmsDocSearchVo cmsDocSearch = new CmsDocSearchVo();
cmsDocSearch.setIsDir(false);
@ -1055,4 +1087,127 @@ public class CmsAsyncServiceImpl implements ICmsAsyncService {
}
}
/**
* 检查是否为积分部门
*
* @param deptId 部门ID
* @return 是否为积分部门
*/
private boolean isPointsDept(Long deptId) {
Long cachedPointsDeptId = redisService.getCacheObject(CacheConstants.ZONE_POINTS_DEPT_ID_KEY);
return cachedPointsDeptId != null && cachedPointsDeptId > 0 && cachedPointsDeptId.equals(deptId);
}
@Async
@Override
public void addPublishDocPointsAsync(Long deptId, String createBy, Long docId, String subjectDesc) {
try {
if (deptId == null || createBy == null) {
logger.warn("文章审核通过增加积分失败deptId或createBy为空docId={}", docId);
return;
}
// 检查是否为积分部门
if (!isPointsDept(deptId)) {
logger.info("文章审核通过增加积分跳过非积分部门deptId={}docId={}", deptId, docId);
return;
}
// 获取用户ID
com.microservices.system.api.domain.SysUser sysUser = FeignUtils.getReturnData(
remoteUserService.getSysUserByUserName(createBy, SecurityConstants.INNER));
if (sysUser == null || sysUser.getUserId() == null) {
logger.warn("文章审核通过增加积分失败未找到用户信息createBy={}docId={}", createBy, docId);
return;
}
// 直接写入 Redis 积分队列 Zone 模块定时任务消费
JSONObject task = new JSONObject();
task.put("sourceType", CmsConstants.PUBLISH_DOC);
task.put("userId", sysUser.getUserId());
task.put("deptId", deptId);
task.put("sourceId", docId);
task.put("subjectDesc", subjectDesc);
task.put("operatorId", sysUser.getUserId());
redisService.leftPush(CmsConstants.ZONE_POINTS_ASYNC_QUEUE_KEY, task.toJSONString());
logger.info("文章审核通过增加积分任务已写入队列docId={}userId={}deptId={}", docId, sysUser.getUserId(), deptId);
} catch (Exception e) {
// 捕获异常确保不影响主事务
logger.error("文章审核通过增加积分异常docId={}", docId, e);
}
}
@Async
@Override
public void addVisitsDocPointsAsync(Long deptId, String createBy, Long docId, String docName, Long totalVisits) {
try {
if (deptId == null || createBy == null) {
logger.warn("文章浏览增加积分失败参数不合法deptId={}createBy={}", deptId, createBy);
return;
}
// 检查是否为积分部门
if (!isPointsDept(deptId)) {
logger.info("文章浏览增加积分跳过非积分部门deptId={}docId={}", deptId, docId);
return;
}
// 获取用户ID
com.microservices.system.api.domain.SysUser sysUser = FeignUtils.getReturnData(
remoteUserService.getSysUserByUserName(createBy, SecurityConstants.INNER));
if (sysUser == null || sysUser.getUserId() == null) {
logger.warn("文章浏览增加积分失败未找到用户信息createBy={}docId={}", createBy, docId);
return;
}
// 直接写入 Redis 积分队列 Zone 模块定时任务消费
JSONObject task = new JSONObject();
task.put("sourceType", CmsConstants.VISITS_DOC);
task.put("userId", sysUser.getUserId());
task.put("deptId", deptId);
task.put("sourceId", docId);
// JSONObject sourceData = new JSONObject();
// sourceData.put("totalVisits", totalVisits);
// sourceData.put("currentIncrement", pointsTriggerCount);
task.put("sourceData", totalVisits);
task.put("subjectDesc", docName);
task.put("operatorId", SecurityUtils.getUserId());
redisService.leftPush(CmsConstants.ZONE_POINTS_ASYNC_QUEUE_KEY, task.toJSONString());
logger.info("文章浏览增加积分任务已写入队列docId={}userId={}deptId={}totalVisits={}", docId, sysUser.getUserId(), deptId, totalVisits);
} catch (Exception e) {
// 捕获异常确保不影响主事务
logger.error("文章浏览增加积分异常docId={}", docId, e);
}
}
@Async
@Override
public void addFeaturedDocPointsAsync(Long deptId, String createBy, Long docId, String subjectDesc) {
try {
if (deptId == null || createBy == null) {
logger.warn("积分-文章精选参数不合法deptId={}createBy={}", deptId, createBy);
return;
}
// 检查是否为积分部门
if (!isPointsDept(deptId)) {
logger.info("积分-文章精选非积分部门deptId={}docId={}", deptId, docId);
return;
}
// 获取用户ID
com.microservices.system.api.domain.SysUser sysUser = FeignUtils.getReturnData(
remoteUserService.getSysUserByUserName(createBy, SecurityConstants.INNER));
if (sysUser == null || sysUser.getUserId() == null) {
logger.warn("积分-文章精选未找到用户信息createBy={}docId={}", createBy, docId);
return;
}
// 直接写入 Redis 积分队列 Zone 模块定时任务消费
JSONObject task = new JSONObject();
task.put("sourceType", CmsConstants.FEATURED_DOC);
task.put("userId", sysUser.getUserId());
task.put("deptId", deptId);
task.put("sourceId", docId);
task.put("subjectDesc", "" + subjectDesc + "》管理员标记");
task.put("operatorId", SecurityUtils.getUserId());
redisService.leftPush(CmsConstants.ZONE_POINTS_ASYNC_QUEUE_KEY, task.toJSONString());
logger.info("文章被设为精选增加积分任务已写入队列docId={}userId={}deptId={}", docId, sysUser.getUserId(), deptId);
} catch (Exception e) {
// 捕获异常确保不影响主事务
logger.error("文章被设为精选增加积分异常docId={}", docId, e);
}
}
}

View File

@ -15,6 +15,7 @@ import com.microservices.cms.utils.CmsGitLinkRequestUrl;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.constant.UserConstants;
import com.microservices.common.core.enums.DocAndZoneOperation;
import com.microservices.common.core.enums.DocAuditStatus;
import com.microservices.common.core.enums.SystemRole;
@ -23,10 +24,12 @@ import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.httpClient.constant.GitLinkConstants;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.auth.AuthUtil;
import com.microservices.common.security.service.TokenService;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteDeptService;
import com.microservices.system.api.RemoteUserService;
@ -47,7 +50,11 @@ import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@ -79,6 +86,8 @@ public class CmsDocServiceImpl implements ICmsDocService {
@Autowired
private RemoteDeptService remoteDeptService;
@Autowired
private TokenService tokenService;
@Autowired
@Lazy
private ICmsDocCommentService cmsDocCommentService;
@Value("${cms.detailPath:/newdetail}")
@ -88,7 +97,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
* 查询文章信息
*
* @param id 文章信息主键
* @param isOpen
* @param isOpen 是否公开访问
* @return 文章信息
*/
@Override
@ -103,7 +112,14 @@ public class CmsDocServiceImpl implements ICmsDocService {
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
String branchName = DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus()) ? CmsConstants.BRANCH_MASTER : cmsDoc.getAuditTmpBranchName();
// 确定查询文件的分支已发布文章或非仓库改动使用 master其他使用临时分支
String branchName;
if (DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())
|| (cmsDoc.getAuditType() != null && cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode()))) {
branchName = CmsConstants.BRANCH_MASTER;
} else {
branchName = cmsDoc.getAuditTmpBranchName();
}
FileEntryDto fileEntryDto = getFileEntryDto(cmsProject, cmsDoc.getFilePath(), branchName);
EntryDto entryDto = fileEntryDto.getEntries();
@ -114,7 +130,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
if (cmsDoc.getPublishTime() != null && cmsDoc.getPublishTime().after(cmsDoc.getCreateTime())) {
cmsDocDto.setIsFirstPublish(false);
}
if (isOpen) {
if (isOpen && DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())) {
updateVisits(cmsDocDto.getId());
}
return cmsDocDto;
@ -170,6 +186,21 @@ public class CmsDocServiceImpl implements ICmsDocService {
return cmsDocList.stream().map(CmsDoc::copyToCmsDocAllDto).collect(Collectors.toList());
}
@Override
public GenericsTableDataInfo<CmsDocAllDto> selectAllDocList(Long deptId, CmsDocSearchVo cmsDocSearchVo) {
List<CmsDocAllDto> list = selectCmsDocListByDept(deptId, cmsDocSearchVo);
list.forEach(x -> {
String docDetailUrl = getDocDetailUrl(x.getDeptId(), x.getId());
x.setDocDetailUrl(docDetailUrl);
Boolean editable = isCmsDocEditable(x);
x.setEditable(editable);
});
int count = selectCmsDocCountByDept(deptId, cmsDocSearchVo);
return new GenericsTableDataInfo<>(list, Long.valueOf(count));
}
@Override
public int selectCmsDocCountByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo) {
cmsDocSearchVo.setDeptId(deptId);
@ -232,14 +263,14 @@ public class CmsDocServiceImpl implements ICmsDocService {
@Override
public List<CmsDocDetailDto> selectCmsDocDetailList(Long dirId) {
List<CmsDocDetailDto> cmsDocDetailDtoList = new ArrayList<>();
CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirById(dirId);
CmsDoc cmsDir = selectCmsDir(dirId);
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDir.getDeptId());
CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
cmsDocSearchVo.setDirName(cmsDir.getName());
cmsDocSearchVo.setDeptId(cmsDir.getDeptId());
cmsDocSearchVo.setIsDir(false);
List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocListSetDefaultHeadImg(cmsDocSearchVo);
List<CompletableFuture<CmsDocDetailDto>> futures = new ArrayList();
List<CompletableFuture<CmsDocDetailDto>> futures = new ArrayList<>();
for (CmsDoc cmsDoc : cmsDocList) {
CompletableFuture<CmsDocDetailDto> future = CompletableFuture.supplyAsync(() -> {
CmsDocDetailDto cmsDocDetailDto = cmsDoc.copyToCmsDocDetailDto();
@ -296,7 +327,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
@Override
public void updateCmsDocShaByPathAndDeptId(String sha, String filePath, Long deptId) {
cmsDocMapper.updateCmsDocShaByPathAndDeptId(sha,filePath,deptId);
cmsDocMapper.updateCmsDocShaByPathAndDeptId(sha, filePath, deptId);
}
private List<CmsDocBaseDto> selectCmsDocListByDatabase(Long dirId, CmsDocSearchVo cmsDocSearchVo) {
@ -313,7 +344,8 @@ public class CmsDocServiceImpl implements ICmsDocService {
/**
* 设置文章默认头图为栏目头图
* @param dirId 栏目id
*
* @param dirId 栏目id
* @param cmsDocSearchVo 文章查询条件
* @return 文章列表
*/
@ -358,12 +390,16 @@ public class CmsDocServiceImpl implements ICmsDocService {
checkCmsLockStatus(dirDoc.getDeptId());
CmsDoc cmsDoc = cmsDocInputDto.toCmsDoc(dirDoc);
if (isNoAuditRequired(dirDoc.getDeptId()) || isCurrentUserZoneAdmin(dirDoc.getDeptId())) {
if (isNoAuditRequired(dirDoc.getDeptId()) || tokenService.hasDeptPerms(dirDoc.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT)) {
//文章自动置为审核通过状态值
cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
boolean success = insertCommonCmsDoc(cmsDoc,
"创建文章:" + StringUtils.truncateStringAndConcatenatingSpecifiedString(cmsDoc.getName(), 100, "..."),
cmsDocInputDto.getContent());
if (success) {
// 自动审核通过时也需要增加积分
cmsAsyncService.addPublishDocPointsAsync(cmsDoc.getDeptId(), cmsDoc.getCreateBy(), cmsDoc.getId(), cmsDoc.getName());
}
return success ? cmsDoc.getId().intValue() : null;
} else {
//创建临时分支创建文件创建PR
@ -401,7 +437,9 @@ public class CmsDocServiceImpl implements ICmsDocService {
public String getCmsDocFileContent(CmsProject cmsProject, CmsDoc cmsDoc) {
String branchName = CmsConstants.BRANCH_MASTER;
if (!DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())) {
branchName = cmsDoc.getAuditTmpBranchName();
if (StringUtils.isNotEmpty(cmsDoc.getAuditTmpBranchName())) {
branchName = cmsDoc.getAuditTmpBranchName();
}
}
//当删除文章为文件夹时判断该文件夹下是否存在文章若存在需先删除所有文章
JSONObject getSubEntriesResult = gitLinkRequestHelper.doGet(
@ -421,9 +459,34 @@ public class CmsDocServiceImpl implements ICmsDocService {
@Transactional(rollbackFor = Exception.class)
public boolean auditCmsDoc(Long docId, CmsDocAuditVo cmsDocAuditVo) {
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(docId);
if (cmsDoc == null) {
throw new ServiceException("该文章不存在");
}
checkCmsLockStatus(cmsDoc.getDeptId());
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
// 设置审核备注
cmsDoc.setRemark(cmsDocAuditVo.getRemark());
if (cmsDocAuditVo.getPass() == 1) {
// 处理非仓库改动的审核通过
if (cmsDoc.getAuditType() != null
&& cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode())) {
Long oldCmsDocId = Long.parseLong(cmsDoc.getAuditType().split("-")[1]);
CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocById(oldCmsDocId);
// 用新文章的非仓库字段更新旧文章
oldCmsDoc.setHeadImg(cmsDoc.getHeadImg());
oldCmsDoc.setSummary(cmsDoc.getSummary());
oldCmsDoc.setKeywords(cmsDoc.getKeywords());
oldCmsDoc.setRemark(cmsDoc.getRemark());
cmsDocMapper.updateCmsDoc(oldCmsDoc);
// 删除待审核记录
cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
return true;
}
//同意合并请求
JSONObject result = mergePullRequestByGitLink(cmsProject, cmsDoc);
if (result.containsKey(GitLinkConstants.STATUS_KEY)) {
@ -445,8 +508,16 @@ public class CmsDocServiceImpl implements ICmsDocService {
}
}
if (cmsDocAuditVo.getPass() == 0) {
// 处理非仓库改动的审核拒绝
if (cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode())) {
handleCmsDocWithNoPassByReedit(cmsDocAuditVo, cmsDoc, cmsProject);
return true;
}
//拒绝GitLink合并请求
JSONObject result = refuseMergePullRequestByGitLink(cmsDoc, cmsProject);
String gitLinkPullRequestNumber = cmsDoc.getAuditPrNumber().split("-")[0];
JSONObject result = gitLinkRequestHelper.doPost(
CmsGitLinkRequestUrl.REFUSE_MERGE_PULL_REQUEST(cmsProject, gitLinkPullRequestNumber), null);
if (result.containsKey(GitLinkConstants.STATUS_KEY)) {
//2.若拒绝GitLink合并请求操作返回正确的状态值,更新数据库
if (result.getInteger(GitLinkConstants.STATUS_KEY).equals(1)) {
@ -493,8 +564,10 @@ public class CmsDocServiceImpl implements ICmsDocService {
if (cmsDocAuditVo.getReedit().equals(0)) {
//删除待审核文章
cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
//删除分支
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
// 删除分支如果有
if (StringUtils.isNotEmpty(cmsDoc.getAuditTmpBranchName())) {
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
}
return;
}
}
@ -506,7 +579,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
int success = deleteCmsDocDatabaseById(ids);
if (success == ids.length) {
//删除临时分支
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
}
}
@ -526,7 +599,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
int success = cmsDocMapper.deleteCmsDocById(newDocId);
if (success > 0) {
//删除临时分支
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
}
}
@ -540,11 +613,13 @@ public class CmsDocServiceImpl implements ICmsDocService {
int success = cmsDocMapper.updateCmsDoc(cmsDoc);
if (success > 0) {
//删除临时分支
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
}
// 推送文章地址到搜索引擎
cmsAsyncService.pushDocDataSearchEngine(cmsDoc);
// 文章审核通过后给作者增加积分异步执行不影响主事务
cmsAsyncService.addPublishDocPointsAsync(cmsDoc.getDeptId(), cmsDoc.getCreateBy(), cmsDoc.getId(), cmsDoc.getName());
}
private JSONObject mergePullRequestByGitLink(CmsProject cmsProject, CmsDoc cmsDoc) {
@ -559,12 +634,6 @@ public class CmsDocServiceImpl implements ICmsDocService {
CmsGitLinkRequestUrl.MERGE_PULL_REQUEST(cmsProject, gitLinkPullRequestId), requestBody);
}
private JSONObject refuseMergePullRequestByGitLink(CmsDoc cmsDoc, CmsProject cmsProject) {
//1.调用gitlink拒绝合并请求接口
return gitLinkRequestHelper.doPost(
CmsGitLinkRequestUrl.REFUSE_MERGE_PULL_REQUEST(cmsProject, cmsDoc.getAuditPrNumber()), null);
}
public static String sha1(String data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
@ -706,11 +775,47 @@ public class CmsDocServiceImpl implements ICmsDocService {
* @return 结果
*/
@Override
public boolean updateCmsDoc(Long id, CmsDocDto cmsDocDto) {
@Transactional(rollbackFor = Exception.class)
public Long updateCmsDoc(Long id, CmsDocDto cmsDocDto) {
CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocById(id);
checkCmsLockStatus(oldCmsDoc.getDeptId());
if (!hasRepoRelatedChanged(oldCmsDoc, cmsDocDto)) {
return cmsDocMapper.updateCmsDoc(oldCmsDoc) > 0;
// 更新非仓库字段到原文章对象
if (StringUtils.isNotEmpty(cmsDocDto.getHeadImg())) {
oldCmsDoc.setHeadImg(cmsDocDto.getHeadImg());
}
if (StringUtils.isNotEmpty(cmsDocDto.getSummary())) {
oldCmsDoc.setSummary(cmsDocDto.getSummary());
}
if (StringUtils.isNotEmpty(cmsDocDto.getKeywords())) {
oldCmsDoc.setKeywords(cmsDocDto.getKeywords());
}
// 管理员或免审核直接更新原记录
if (tokenService.hasDeptPerms(oldCmsDoc.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT)
|| isNoAuditRequired(oldCmsDoc.getDeptId())) {
cmsDocMapper.updateCmsDoc(oldCmsDoc);
return oldCmsDoc.getId();
}
// 需要审核创建新的待审核记录与仓库改动逻辑一致
if (oldCmsDoc.getAuditStatus().equals(DocAuditStatus.AUDIT_PASS.getAuditCode())) {
CmsDoc newDocWithAudit = insertDocWithAudit(oldCmsDoc,
DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode() + "-" + oldCmsDoc.getId(),
DocAuditStatus.TO_BE_AUDIT.getAuditCode());
return newDocWithAudit.getId();
}
oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
if (oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_CREATE.getOperationCode())
|| oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_UPDATE.getOperationCode())) {
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(oldCmsDoc.getDeptId());
// 重新开启PR
String gitLinkPullRequestNumber = oldCmsDoc.getAuditPrNumber().split("-")[0];
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.REOPEN_PULL_REQUEST(cmsProject, gitLinkPullRequestNumber), null);
}
// 待审核/未通过文章重新编辑
cmsDocMapper.updateCmsDoc(oldCmsDoc);
return oldCmsDoc.getId();
}
// 当文章名称发生更改时判断文章名是否在当前栏目下已存在
if (StringUtils.isNotEmpty(cmsDocDto.getName()) && !cmsDocDto.getName().equals(oldCmsDoc.getName())) {
@ -725,14 +830,14 @@ public class CmsDocServiceImpl implements ICmsDocService {
}
// 当用户更新栏目时需检查新栏目下是否已存在相同文章名
if(cmsDocDto.getDirId()!=null){
if (cmsDocDto.getDirId() != null) {
CmsDir cmsDir = selectCmsDirById(cmsDocDto.getDirId(), false);
if(cmsDir!=null&&!cmsDir.getName().equals(oldCmsDoc.getDirName())){
if (cmsDir != null && !cmsDir.getName().equals(oldCmsDoc.getDirName())) {
checkDocName(cmsDocDto.getName(), oldCmsDoc.getDeptId(), cmsDir.getName());
}
}
//用户为专区管理员或专区无审核机制时直接更新文章及仓库主分支
if (isCurrentUserZoneAdmin(oldCmsDoc.getDeptId()) || isNoAuditRequired(oldCmsDoc.getDeptId())) {
if (tokenService.hasDeptPerms(oldCmsDoc.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT) || isNoAuditRequired(oldCmsDoc.getDeptId())) {
//专区管理员重新编辑并提交审核未通过文章 重新编辑并提交待审核文章时专区不需审核机制
if (!oldCmsDoc.getAuditStatus().equals(DocAuditStatus.AUDIT_PASS.getAuditCode())) {
//文章审核类型update-原文章id 取出原文章id直接更新删除待审核更新类型文章
@ -747,7 +852,8 @@ public class CmsDocServiceImpl implements ICmsDocService {
if (oldCmsDoc.getAuditType() != null && oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_CREATE.getOperationCode())) {
deleteTmpBranchIfBranchExist(oldCmsDoc);
cmsDocMapper.deleteCmsDocById(id);
return directlyInsertAuditedCmsDocWhenZoneHasNoAuditRequired(cmsDocDto, oldCmsDoc);
directlyInsertAuditedCmsDocWhenZoneHasNoAuditRequired(cmsDocDto, oldCmsDoc);
return oldCmsDoc.getId();
}
}
String branchName = CmsConstants.BRANCH_MASTER;
@ -755,16 +861,52 @@ public class CmsDocServiceImpl implements ICmsDocService {
oldCmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
oldCmsDoc.setAuditTmpBranchName(null);
oldCmsDoc.setKeywords(cmsDocDto.getKeywords());
return updateCmsCommonDatabase(oldCmsDoc, CmsConstants.BRANCH_MASTER) > 0;
updateCmsCommonDatabase(oldCmsDoc, CmsConstants.BRANCH_MASTER);
return oldCmsDoc.getId();
} else {
CmsDoc newDocWithAudit = new CmsDoc();
//待审核未通过文章重新编辑并提交
if (DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(oldCmsDoc.getAuditStatus())
|| DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(oldCmsDoc.getAuditStatus())) {
// 非仓库改动待审核记录检查是否有仓库改动
if (oldCmsDoc.getAuditType() != null
&& oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode())) {
if (!hasRepoRelatedChanged(oldCmsDoc, cmsDocDto)) {
// 纯非仓库改动继续在当前记录上更新
oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
if (StringUtils.isNotEmpty(cmsDocDto.getHeadImg())) {
oldCmsDoc.setHeadImg(cmsDocDto.getHeadImg());
}
if (StringUtils.isNotEmpty(cmsDocDto.getSummary())) {
oldCmsDoc.setSummary(cmsDocDto.getSummary());
}
if (StringUtils.isNotEmpty(cmsDocDto.getKeywords())) {
oldCmsDoc.setKeywords(cmsDocDto.getKeywords());
}
cmsDocMapper.updateCmsDoc(oldCmsDoc);
return oldCmsDoc.getId();
} else {
// 有仓库改动转为仓库改动类型删除当前记录重新创建
Long oldCmsDocId = Long.parseLong(oldCmsDoc.getAuditType().split("-")[1]);
CmsDoc originCmsDoc = cmsDocMapper.selectCmsDocById(oldCmsDocId);
cmsDocMapper.deleteCmsDocById(oldCmsDoc.getId());
// 按照仓库改动逻辑重新创建
newDocWithAudit = insertDocWithAudit(originCmsDoc,
DocAndZoneOperation.OPERATION_UPDATE.getOperationCode() + "-" + oldCmsDocId,
DocAuditStatus.TO_BE_AUDIT.getAuditCode());
cmsAsyncService.asyncUpdateBranchAndFileAndCreatePR(newDocWithAudit, cmsDocDto);
return newDocWithAudit.getId();
}
}
// 仓库改动待审核记录继续在当前记录上更新
oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
newDocWithAudit = oldCmsDoc;
} else
} else {
//更新已发布文章
if (oldCmsDoc.getAuditStatus().equals(DocAuditStatus.AUDIT_PASS.getAuditCode())) {
//1.创建一条auditType="update-原文章id"audit_status为2其余字段与待更新文章一致的新文章数据
@ -772,8 +914,9 @@ public class CmsDocServiceImpl implements ICmsDocService {
DocAndZoneOperation.OPERATION_UPDATE.getOperationCode() + "-" + id,
DocAuditStatus.TO_BE_AUDIT.getAuditCode());
}
}
cmsAsyncService.asyncUpdateBranchAndFileAndCreatePR(newDocWithAudit, cmsDocDto);
return true;
return newDocWithAudit.getId();
}
}
@ -825,7 +968,7 @@ public class CmsDocServiceImpl implements ICmsDocService {
private void deleteTmpBranchIfBranchExist(CmsDoc cmsDoc) {
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
try {
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
} catch (ServiceException e) {
//忽略分支不存在异常
if (!"分支不存在!".equals(e.getMessage())) {
@ -856,15 +999,29 @@ public class CmsDocServiceImpl implements ICmsDocService {
}
private CmsDoc insertDocWithAudit(CmsDoc oldCmsDoc, String auditType, String auditStatus) {
List<CmsDoc> tmpDocList = getTmpDocList(oldCmsDoc);
if (!tmpDocList.isEmpty()) {
throw new ServiceException("已存在待审核操作,请等待审核完成后再进行操作");
}
oldCmsDoc.setId(null);
oldCmsDoc.setAuditType(auditType);
oldCmsDoc.setAuditStatus(auditStatus);
oldCmsDoc.setCreateBy(SecurityUtils.getUsername());
// repo 改动显式设置 auditTmpBranchName null没有对应的分支
if (auditType != null && auditType.contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode())) {
oldCmsDoc.setAuditTmpBranchName(null);
}
cmsDocMapper.insertCmsDoc(oldCmsDoc);
return oldCmsDoc;
}
private CmsDoc insertDeletedDocWithAudit(CmsDoc oldCmsDoc, Long oldCmsDocId) {
List<CmsDoc> tmpDocList = getTmpDocList(oldCmsDoc);
if (!tmpDocList.isEmpty()) {
throw new ServiceException("已存在待审核操作,请等待审核完成后再进行操作");
}
oldCmsDoc.setId(null);
oldCmsDoc.setAuditType(DocAndZoneOperation.OPERATION_DELETE.getOperationCode() + "-" + oldCmsDocId);
oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
@ -1010,7 +1167,13 @@ public class CmsDocServiceImpl implements ICmsDocService {
visits = visits + cmsDoc.getVisits();
}
cmsDoc.setVisits(visits);
return cmsDocMapper.updateCmsDoc(cmsDoc) > 0;
int rows = cmsDocMapper.updateCmsDoc(cmsDoc);
if (rows > 0) {
cmsAsyncService.addVisitsDocPointsAsync(cmsDoc.getDeptId(), cmsDoc.getCreateBy(),
cmsDoc.getId(), cmsDoc.getName(), cmsDoc.getVisits());
return true;
}
return false;
}
@Override
@ -1134,6 +1297,9 @@ public class CmsDocServiceImpl implements ICmsDocService {
//首页推荐时需将顺序设置为1
setCmsSort(1, cmsDoc, false, null);
cmsDoc.setIsHomepage(CmsConstants.TRUE);
// 文章被设为精选异步增加作者积分
cmsAsyncService.addFeaturedDocPointsAsync(cmsDoc.getDeptId(), cmsDoc.getCreateBy(),
cmsDoc.getId(), cmsDoc.getName());
}
return cmsDocMapper.updateCmsDoc(cmsDoc);
}
@ -1157,6 +1323,41 @@ public class CmsDocServiceImpl implements ICmsDocService {
return cmsDocBaseDtoList;
}
@Override
public List<CmsDocBaseDto> selectHomePageDocMergeList(Long zoneId, List<Long> topDocIds) {
Long deptId = getDeptIdByZoneId(zoneId);
Set<Long> addedDocIds = new HashSet<>();
// 1. 获取首页推荐文章列表
List<CmsDocBaseDto> homePageDocs = selectHomePageDocList(deptId);
List<CmsDocBaseDto> result = new ArrayList<>();
// 2. 批量查询置顶文章完整数据按传入顺序放入结果头部
if (topDocIds != null && !topDocIds.isEmpty()) {
List<CmsDoc> topCmsDocs = cmsDocMapper.selectCmsDocByIds(topDocIds);
Map<Long, CmsDoc> topDocMap = new HashMap<>();
for (CmsDoc doc : topCmsDocs) {
topDocMap.put(doc.getId(), doc);
}
for (Long docId : topDocIds) {
CmsDoc doc = topDocMap.get(docId);
if (doc != null) {
result.add(doc.copyToCmsDocBaseDto());
addedDocIds.add(docId);
}
}
}
// 3. 追加首页推荐文章跳过已在置顶列表中的
for (CmsDocBaseDto doc : homePageDocs) {
if (!addedDocIds.contains(doc.getId())) {
result.add(doc);
}
}
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public CmsDir initCmsDir(Long deptId, CmsDir cmsDirInput) {
@ -1303,6 +1504,17 @@ public class CmsDocServiceImpl implements ICmsDocService {
cmsAsyncService.updateCmsPublishDataByGitLink(cmsProject, cmsDoc, branchName);
}
private int deleteCmsDir(CmsProject cmsProject, CmsDoc cmsDir) {
if (!isNoAuditRequired(cmsDir.getDeptId()) || tokenService.hasDeptPerms(cmsDir.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT)) {
boolean deleteResult = cmsAsyncService.deleteCmsDocForGitLink(cmsDir, cmsProject, CmsConstants.BRANCH_MASTER);
if (deleteResult) {
return cmsDocMapper.deleteCmsDocById(cmsDir.getId());
}
throw new ServiceException("栏目删除失败");
}
throw new ServiceException("无权限删除栏目");
}
@Override
public int deleteCmsDocById(Long id) {
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
@ -1312,22 +1524,42 @@ public class CmsDocServiceImpl implements ICmsDocService {
checkCmsLockStatus(cmsDoc.getDeptId());
//2获取专区项目
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
if (cmsDoc.getIsDir()) {
return deleteCmsDir(cmsProject, cmsDoc);
}
//删除待审核审核未通过文章删除临时分支拒绝合并请求
if (DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(cmsDoc.getAuditStatus())
|| DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(cmsDoc.getAuditStatus())) {
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
cmsDocMapper.deleteCmsDocById(id);
return 1;
if (DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(cmsDoc.getAuditStatus())) {
// 非仓库改动类型直接删除
if (cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_NON_REPO_UPDATE.getOperationCode())) {
return cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
}
//拒绝GitLink合并请求
String gitLinkPullRequestNumber = cmsDoc.getAuditPrNumber().split("-")[0];
gitLinkRequestHelper.doPost(
CmsGitLinkRequestUrl.REFUSE_MERGE_PULL_REQUEST(cmsProject, gitLinkPullRequestNumber), null);
gitLinkRequestHelper.doPost(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()), null);
return cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
}
if (isCurrentUserZoneAdmin(cmsDoc.getDeptId()) //当前用户为专区管理员
if (DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(cmsDoc.getAuditStatus())) {
return cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
}
// 判读当前用户是否拥有文章直接删除权限
if (tokenService.hasDeptPerms(cmsDoc.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT) //当前用户为专区管理员
|| isNoAuditRequired(cmsDoc.getDeptId()) //当前专区文章发布无需审核机制
) {
String branchName = CmsConstants.BRANCH_MASTER;
boolean deleteResult = cmsAsyncService.deleteCmsDocForGitLink(cmsDoc, cmsProject, branchName);
if (deleteResult) {
return deleteCmsDocDatabaseById(cmsDoc.getId());
// 拒绝相关合并请求并清理所有临时分支同时删除所有临时申请
List<CmsDoc> tmpDocList = getTmpDocList(cmsDoc);
cmsAsyncService.clearTmpDoc(cmsProject, tmpDocList);
cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_FAIL.getAuditCode());
cmsDoc.setRemark("管理员删除");
return cmsDocMapper.updateCmsDoc(cmsDoc);
}
} else {
//1.创建一条auditType="delete-原文章id",audit_status=2其余字段与待删除文章一致的新文章数据
CmsDoc newDocWithAudit = insertDeletedDocWithAudit(cmsDoc, id);
@ -1337,6 +1569,18 @@ public class CmsDocServiceImpl implements ICmsDocService {
throw new ServiceException("文件删除失败");
}
@Override
public List<CmsDoc> getTmpDocList(CmsDoc cmsDoc) {
CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
cmsDocSearchVo.setName(cmsDoc.getName());
cmsDocSearchVo.setDeptId(cmsDoc.getDeptId());
cmsDocSearchVo.setIsDir(false);
cmsDocSearchVo.setDirName(cmsDoc.getDirName());
cmsDocSearchVo.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
// 拒绝相关合并请求并清理所有临时分支同时删除所有临时申请
return cmsDocMapper.selectCmsDocList(cmsDocSearchVo);
}
public int deleteCmsDocDatabaseById(Long id) {
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
if (cmsDoc != null && !cmsDoc.getIsDir()) {

View File

@ -49,6 +49,16 @@ public class CmsProjectController extends BaseController {
return R.ok(changeRoleResult);
}
@InnerAuth
@PutMapping("/changeOrgRole/user/{userId}/operate/{operate}")
@ApiOperation(value = "修改用户所在GItLink组织中的权限", hidden = true)
public R<Boolean> changeUserOrgRole(@PathVariable("userId") Long userId, @PathVariable("operate") Long operate
) {
Boolean changeRoleResult = cmsProjectService.changeUserOrgRole(userId, operate);
return R.ok(changeRoleResult);
}
/**
* 查询专区对应项目列表
*/

View File

@ -0,0 +1,21 @@
package com.microservices.cms.project.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author otto
*/
@Data
public class GitlinkOrganizationTeamVo implements Serializable {
@ApiModelProperty(value = "团队权限")
private String authorize;
@ApiModelProperty(value = "团队Id")
private Long id;
@ApiModelProperty(value = "团队名称")
private String name;
@ApiModelProperty(value = "团队昵称")
private String nickname;
}

View File

@ -2,6 +2,7 @@ package com.microservices.cms.project.mapper;
import com.microservices.cms.project.domain.CmsProject;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -64,4 +65,25 @@ public interface CmsProjectMapper {
CmsProject selectCmsProjectByDeptId(Long deptId);
CmsProject selectCmsProjectByIdentifier(String identifier);
/**
* 全局搜索 - 全文索引查询
*
* @param keyword 搜索关键词
* @param deptId 组织ID可选
* @param offset 偏移量
* @param pageSize 每页数量
* @return 搜索结果列表
*/
List<CmsProject> globalSearch(@Param("keyword") String keyword, @Param("deptId") Long deptId,
@Param("offset") int offset, @Param("pageSize") int pageSize);
/**
* 全局搜索结果计数
*
* @param keyword 搜索关键词
* @param deptId 组织ID可选
* @return 结果数量
*/
Long globalSearchCount(@Param("keyword") String keyword, @Param("deptId") Long deptId);
}

View File

@ -82,4 +82,6 @@ public interface ICmsProjectService {
boolean initZoneCmsProjectByDept(Long deptId);
CmsProject selectCmsProjectByDeptIdAndNoException(Long deptId);
Boolean changeUserOrgRole(Long userId, Long operate);
}

View File

@ -6,6 +6,7 @@ import com.microservices.cms.doc.domain.*;
import com.microservices.cms.doc.service.ICmsAsyncService;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.cms.project.domain.CmsProject;
import com.microservices.cms.project.domain.GitlinkOrganizationTeamVo;
import com.microservices.cms.project.mapper.CmsProjectMapper;
import com.microservices.cms.project.service.ICmsProjectService;
import com.microservices.cms.utils.CmsConstants;
@ -320,6 +321,40 @@ public class CmsProjectServiceImpl implements ICmsProjectService {
return cmsProjectMapper.selectCmsProjectByDeptId(deptId);
}
@Override
public Boolean changeUserOrgRole(Long userId, Long operate) {
Long gitlinkOrgId = getOrganizationId();
SysUser sysUser = FeignUtils.getReturnData(remoteUserService.getSysUserByUserId(userId, SecurityConstants.INNER));
if (sysUser == null) {
return false;
}
List<GitlinkOrganizationTeamVo> gitlinkOrganizationTeamVoList = gitLinkRequestHelper.getAllDataByPage(
CmsGitLinkRequestUrl.TEAMS_BY_ORGANIZATION_URL(gitlinkOrgId), "teams", GitlinkOrganizationTeamVo.class
);
GitlinkOrganizationTeamVo ownerTeam = gitlinkOrganizationTeamVoList.stream().filter(x -> "owner".equals(x.getAuthorize())).findFirst().orElse(null);
if (ownerTeam == null) {
return false;
}
if (operate == 1) {
JSONObject addUserToTeam = new JSONObject();
addUserToTeam.put("username", sysUser.getUserName());
gitLinkRequestHelper.doPost(
CmsGitLinkRequestUrl.ADD_USER_TO_ORGANIZATION_TEAM(gitLinkOrganization, ownerTeam.getId()), addUserToTeam);
}
if (operate == 3) {
try {
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_USER_FROM_ORGANIZATION_TEAM(
gitLinkOrganization,
ownerTeam.getId(),
sysUser.getUserName()
));
} catch (Exception e) {
return false;
}
}
return false;
}
@Override
public Boolean changeUserRole(Long userId, Long oldDeptId, Long newDeptId, Long oldRoleId, Long newRoleId) {
SysUser sysUser = FeignUtils.getReturnData(remoteUserService.getSysUserByUserId(userId, SecurityConstants.INNER));
@ -338,9 +373,10 @@ public class CmsProjectServiceImpl implements ICmsProjectService {
try {
gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.REMOVE_USER_FROM_PROJECT(oldCmsProject), requestBody);
} catch (ServiceException e) {
if (!e.getMessage().contains("用户为组织成员,请到组织下操作")) {
throw new ServiceException(e.getMessage());
}
log.error("将用户从原项目中移除发生异常:{}", e.getMessage());
// if (!e.getMessage().contains("用户为组织成员,请到组织下操作")||!e.getMessage().contains("还不是项目成员")) {
// throw new ServiceException(e.getMessage());
// }
}
}
//将用户加入新的项目

View File

@ -0,0 +1,43 @@
package com.microservices.cms.search.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 文章搜索请求对象
*
* @author microservices
*/
@Data
@ApiModel("文章搜索请求对象")
public class DocSearchRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 搜索关键词
*/
@ApiModelProperty(value = "搜索关键词", required = true)
private String keyword;
/**
* 专区ID
*/
@ApiModelProperty(value = "专区ID")
private Long zoneId;
/**
* 页码
*/
@ApiModelProperty(value = "页码")
private Integer pageNum = 1;
/**
* 每页数量
*/
@ApiModelProperty(value = "每页数量")
private Integer pageSize = 20;
}

View File

@ -0,0 +1,63 @@
package com.microservices.cms.search.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 全局搜索响应对象
*
* @author microservices
*/
@Data
@ApiModel("文章搜索响应对象")
public class DocSearchResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 总结果数
*/
@ApiModelProperty(value = "总结果数")
private Long total;
/**
* 搜索结果列表
*/
@ApiModelProperty(value = "搜索结果列表")
private List<DocSearchResult> results;
/**
* 各类型结果数量统计
*/
@ApiModelProperty(value = "各类型结果数量统计")
private Map<String, Long> typeCounts;
/**
* 搜索耗时毫秒
*/
@ApiModelProperty(value = "搜索耗时(毫秒)")
private Long searchTime;
/**
* 搜索关键词
*/
@ApiModelProperty(value = "搜索关键词")
private String keyword;
/**
* 当前页码
*/
@ApiModelProperty(value = "当前页码")
private Integer pageNum;
/**
* 每页数量
*/
@ApiModelProperty(value = "每页数量")
private Integer pageSize;
}

View File

@ -0,0 +1,65 @@
package com.microservices.cms.search.dto;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 文章搜索结果对象
*
* @author microservices
*/
@Data
@ApiModel("文章搜索结果对象")
public class DocSearchResult implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ApiModelProperty(value = "ID")
private Long id;
/**
* 标题
*/
@ApiModelProperty(value = "标题")
private String title;
/**
* 内容摘要
*/
@ApiModelProperty(value = "内容摘要")
private String content;
/**
* 组织ID
*/
@ApiModelProperty(value = "组织ID")
private Long deptId;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
private Date createTime;
/**
* 相关度评分
*/
@ApiModelProperty(value = "相关度评分")
private Double score;
/**
* 扩展数据JSON格式
*/
@ApiModelProperty(value = "扩展数据")
private JSONObject extendData;
}

View File

@ -0,0 +1,20 @@
package com.microservices.cms.search.service;
import com.microservices.cms.search.dto.DocSearchRequest;
import com.microservices.cms.search.dto.DocSearchResponse;
/**
* 全局搜索服务接口
*
* @author microservices
*/
public interface IGlobalSearchService {
/**
* 全局搜索
*
* @param request 搜索请求
* @return 搜索响应
*/
DocSearchResponse docSearch(DocSearchRequest request);
}

View File

@ -0,0 +1,215 @@
package com.microservices.cms.search.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.cms.doc.domain.CmsDoc;
import com.microservices.cms.doc.domain.CmsDocWithScore;
import com.microservices.cms.doc.mapper.CmsDocMapper;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.cms.search.service.IGlobalSearchService;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.redis.service.RedisService;
import com.microservices.cms.search.dto.DocSearchRequest;
import com.microservices.cms.search.dto.DocSearchResponse;
import com.microservices.cms.search.dto.DocSearchResult;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.model.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* 全局搜索服务实现
*
* @author microservices
*/
@Service
public class GlobalSearchServiceImpl implements IGlobalSearchService {
private static final Logger logger = LoggerFactory.getLogger(GlobalSearchServiceImpl.class);
@Autowired
private CmsDocMapper cmsDocMapper;
@Autowired
private RedisService redisService;
@Autowired
private ICmsDocService cmsDocService;
private static final String SEARCH_CACHE_PREFIX = "search:cache:";
private static final Long CACHE_EXPIRE_SECONDS = 300L; // 5分钟
@Override
public DocSearchResponse docSearch(DocSearchRequest request) {
long startTime = System.currentTimeMillis();
// 参数校验
if (StringUtils.isEmpty(request.getKeyword())) {
return buildEmptyResponse(request);
}
// 1. 尝试从缓存获取仅第一页
if (request.getPageNum() == 1) {
String cacheKey = buildCacheKey(request);
DocSearchResponse cached = redisService.getCacheObject(SEARCH_CACHE_PREFIX + cacheKey);
if (cached != null) {
cached.setSearchTime(System.currentTimeMillis() - startTime);
return cached;
}
}
// 2. 并行搜索各类型
List<DocSearchResult> allResults = new ArrayList<>();
Map<String, Long> typeCounts = new HashMap<>();
// 获取当前用户信息
LoginUser loginUser = SecurityUtils.getLoginUser();
// 搜索CMS文章
List<DocSearchResult> docResults = searchDocs(request);
allResults.addAll(docResults);
typeCounts.put("doc", (long) docResults.size());
// 3. 按相关度评分排序
allResults.sort((a, b) -> {
if (b.getScore() != null && a.getScore() != null) {
return b.getScore().compareTo(a.getScore());
}
return 0;
});
// 4. 分页
int total = allResults.size();
int start = (request.getPageNum() - 1) * request.getPageSize();
int end = Math.min(start + request.getPageSize(), total);
List<DocSearchResult> pagedResults = start < total ?
allResults.subList(start, end) : new ArrayList<>();
// 5. 构建响应
DocSearchResponse response = new DocSearchResponse();
response.setTotal((long) total);
response.setResults(pagedResults);
response.setTypeCounts(typeCounts);
response.setKeyword(request.getKeyword());
response.setPageNum(request.getPageNum());
response.setPageSize(request.getPageSize());
response.setSearchTime(System.currentTimeMillis() - startTime);
// 6. 缓存结果仅第一页
if (request.getPageNum() == 1) {
String cacheKey = buildCacheKey(request);
redisService.setCacheObject(SEARCH_CACHE_PREFIX + cacheKey, response, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
}
// 7. 记录搜索日志
recordSearchLog(request, response, loginUser);
return response;
}
/**
* 记录搜索日志
*/
private void recordSearchLog(DocSearchRequest request, DocSearchResponse response, LoginUser loginUser) {
Long userId = loginUser != null ? loginUser.getUserId() : null;
String searchTypes = "文章";
Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());
logger.info("全局搜索 - 关键词: {}, 用户ID: {}, 耗时: {}ms, 结果数: {}, 类型: {}, 专区Id: {}, 组织ID: {}",
request.getKeyword(),
userId,
response.getSearchTime(),
response.getTotal(),
searchTypes,
request.getZoneId(),
deptId
);
}
/**
* 搜索CMS文章
*/
private List<DocSearchResult> searchDocs(DocSearchRequest request) {
List<DocSearchResult> results = new ArrayList<>();
try {
// 使用部门ID参数
Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());
// 使用新的带评分的查询方法
List<CmsDocWithScore> docs = cmsDocMapper.globalSearchWithScore(
request.getKeyword(),
deptId,
(request.getPageNum() - 1) * request.getPageSize(),
request.getPageSize()
);
for (CmsDocWithScore doc : docs) {
DocSearchResult result = new DocSearchResult();
result.setId(doc.getId());
result.setTitle(highlightKeyword(doc.getName(), request.getKeyword()));
result.setContent(highlightKeyword(doc.getSummary(), request.getKeyword()));
result.setDeptId(doc.getDeptId());
result.setCreateTime(doc.getCreateTime());
// 使用数据库返回的实际搜索评分
Double score = doc.getSearchScore();
if (score == null || score == 0.0) {
// 对于 LIKE 匹配无全文评分赋予最小评分
score = 0.1;
}
result.setScore(score);
JSONObject extendData = new JSONObject();
extendData.put("visits", doc.getVisits());
extendData.put("publishTime", doc.getPublishTime());
result.setExtendData(extendData);
results.add(result);
}
} catch (Exception e) {
// 记录错误但继续处理其他类型
System.err.println("搜索CMS文章失败: " + e.getMessage());
}
return results;
}
/**
* 构建缓存键
*/
private String buildCacheKey(DocSearchRequest request) {
Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());
StringBuilder key = new StringBuilder(request.getKeyword());
key.append(":").append(String.join(",", "Doc"));
if (deptId != null) {
key.append(":dept=").append(deptId);
}
return key.toString();
}
/**
* 构建空响应
*/
private DocSearchResponse buildEmptyResponse(DocSearchRequest request) {
DocSearchResponse response = new DocSearchResponse();
response.setTotal(0L);
response.setResults(new ArrayList<>());
response.setTypeCounts(new HashMap<>());
response.setKeyword(request.getKeyword());
response.setPageNum(request.getPageNum());
response.setPageSize(request.getPageSize());
response.setSearchTime(0L);
return response;
}
/**
* 将文本中的关键词用 <em> 标签包裹不区分大小写
*/
private String highlightKeyword(String text, String keyword) {
if (StringUtils.isEmpty(text) || StringUtils.isEmpty(keyword)) {
return text;
}
return text.replaceAll("(?i)(" + Pattern.quote(keyword) + ")", "<em>$1</em>");
}
}

View File

@ -25,4 +25,24 @@ public class CmsConstants {
*
*/
public final static String TRUE = "1";
/**
* 专区积分异步队列 Redis Key Zone 模块保持一致
*/
public final static String ZONE_POINTS_ASYNC_QUEUE_KEY = "zone:points:async:queue";
/**
* 发布文章积分类型
*/
public final static String PUBLISH_DOC = "publishDoc";
/**
* 浏览文章积分类型
*/
public final static String VISITS_DOC = "visitsDoc";
/**
* 精选文章积分类型
*/
public final static String FEATURED_DOC = "featuredDoc";
}

View File

@ -12,6 +12,18 @@ import java.util.Map;
public class CmsGitLinkRequestUrl extends GitLinkRequestUrl {
public static GitLinkRequestUrl ADD_USER_TO_ORGANIZATION_TEAM(String orgIdentifier, Long teamId) {
return getAdminGitLinkRequestUrl(String.format("/api/organizations/%s/teams/%d/team_users.json", orgIdentifier, teamId));
}
public static String TEAMS_BY_ORGANIZATION_URL(Long orgId) {
return String.format("/api/organizations/%s/teams.json", orgId);
}
public static GitLinkRequestUrl DELETE_USER_FROM_ORGANIZATION_TEAM(String orgIdentifier, Long teamId, String username) {
return getAdminGitLinkRequestUrl(String.format("/api/organizations/%s/teams/%d/team_users/%s.json", orgIdentifier, teamId, username));
}
/**
* 创建项目
*/
@ -66,7 +78,7 @@ public class CmsGitLinkRequestUrl extends GitLinkRequestUrl {
public static GitLinkRequestUrl DELETE_BRANCH(CmsProject cmsProject, String branchName) {
return getGitLinkRequestUrl(
String.format("/api/v1/%s/%s/branches/%s.json", cmsProject.getUsername(), cmsProject.getIdentifier(), branchName)
String.format("/api/v1/%s/%s/branches/delete.json?branch_name=%s", cmsProject.getUsername(), cmsProject.getIdentifier(), branchName)
);
}
@ -76,6 +88,12 @@ public class CmsGitLinkRequestUrl extends GitLinkRequestUrl {
);
}
public static GitLinkRequestUrl REOPEN_PULL_REQUEST(CmsProject cmsProject, String giteaPrNumber) {
return getGitLinkRequestUrl(
String.format("/api/v1/%s/%s/pulls/%s/reopen", cmsProject.getUsername(), cmsProject.getIdentifier(), giteaPrNumber)
);
}
public static GitLinkRequestUrl GET_PULL_REQUEST_DETAILS(CmsProject cmsProject, String giteaPrNumber) {
return getGitLinkRequestUrl(
String.format("/api/%s/%s/pulls/%s.json", cmsProject.getUsername(), cmsProject.getIdentifier(), giteaPrNumber)
@ -101,7 +119,7 @@ public class CmsGitLinkRequestUrl extends GitLinkRequestUrl {
}
public static GitLinkRequestUrl ADD_USER_FROM_PROJECT(CmsProject cmsProject) {
return getGitLinkRequestUrl(
return getAdminGitLinkRequestUrl(
String.format("/api/%s/%s/collaborators.json", cmsProject.getUsername(), cmsProject.getIdentifier())
);
}

View File

@ -37,6 +37,7 @@
<result property="auditTmpBranchName" column="audit_tmp_branch_name"/>
<result property="auditPrNumber" column="audit_pr_number"/>
<result property="keywords" column="keywords"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="selectCmsDocVo">
@ -71,7 +72,8 @@
audit_type,
audit_tmp_branch_name,
keywords,
audit_pr_number
audit_pr_number,
remark
from cms_doc
</sql>
@ -103,7 +105,51 @@
cd.is_homepage,
cd.editor_type,
cd.keywords,
cd.is_dir
cd.is_dir,
cd.audit_status,
cd.audit_type,
cd.audit_tmp_branch_name,
cd.audit_pr_number,
cd.remark
from cms_doc cd
</sql>
<sql id="selectCmsDocWithScoreVo">
select cd.id,
cd.create_by,
cd.create_time,
cd.update_by,
cd.update_time,
cd.name,
cd.file_name,
cd.file_path,
cd.file_sha,
cd.file_size,
cd.file_url,
cd.html_url,
cd.visits,
cd.sort,
cd.publish_time,
cd.publish_user,
cd.publish_user_image,
cd.publish_user_url,
cd.publish_user_nickname,
cd.summary,
cd.head_img,
cd.dir_name,
cd.dept_id,
cd.issue_id,
cd.is_homepage,
cd.editor_type,
cd.keywords,
cd.is_dir,
cd.audit_status,
cd.audit_type,
cd.audit_tmp_branch_name,
cd.audit_pr_number,
-- 计算全文搜索相关度评分
MATCH(cd.name, cd.summary) AGAINST(#{keyword} IN NATURAL LANGUAGE MODE) AS search_score,
cd.remark
from cms_doc cd
</sql>
@ -141,7 +187,12 @@
cd.is_homepage,
cd.editor_type,
cd.keywords,
cd.is_dir
cd.is_dir,
cd.audit_status,
cd.audit_type,
cd.audit_tmp_branch_name,
cd.audit_pr_number,
cd.remark
from cms_doc cd
left join cms_doc dir on cd.dir_name = dir.name and cd.dept_id = dir.dept_id and dir.is_dir=1
</sql>
@ -166,10 +217,20 @@
where id = #{id}
</select>
<select id="selectCmsDocByIds" resultMap="CmsDocResult">
<include refid="selectCmsDocVo"/>
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and audit_status = '1'
</select>
<select id="selectCmsDocByNameAndDeptId" resultMap="CmsDocResult">
<include refid="selectCmsDocVo"/>
<where>
and name = #{name}
and audit_status != 0
and dept_id = #{deptId}
<choose>
<when test="dirName != null">
@ -181,6 +242,10 @@
</choose>
</where>
</select>
<select id="selectCmsDocByNameAndDeptIdIgnoreDirName" resultMap="CmsDocResult">
<include refid="selectCmsDocVo"/>
where name = #{name} and dept_id = #{deptId} and is_dir = 0
</select>
<select id="selectCmsDocDirByNameAndDeptId" resultMap="CmsDocResult">
<include refid="selectCmsDocVo"/>
where name = #{name} and dept_id = #{deptId} and is_dir = 1
@ -221,6 +286,7 @@
<if test="auditStatus != null and auditStatus != '-1' ">and audit_status = #{auditStatus}</if>
<if test="auditStatus == null ">and audit_status = '1'</if>
<if test="createBy != null and createBy != '' ">and create_by = #{createBy}</if>
<if test="excludeId != null ">and id != #{excludeId}</if>
</where>
</sql>
@ -458,6 +524,7 @@
<if test="auditTmpBranchName != null">audit_tmp_branch_name = #{auditTmpBranchName},</if>
<if test="auditPrNumber != null">audit_pr_number = #{auditPrNumber},</if>
<if test="keywords != null">keywords = #{keywords},</if>
<if test="remark != null">remark = #{remark},</if>
</trim>
where id = #{id}
</update>
@ -494,4 +561,70 @@
#{id}
</foreach>
</delete>
<!-- 全局搜索 - 全文索引查询(含 LIKE 后备搜索) -->
<select id="globalSearch" resultMap="CmsDocResult">
<include refid="selectCmsDocAliasVo"/>
WHERE
cd.is_dir = 0
AND cd.audit_status = '1'
AND (
MATCH(cd.name, cd.summary) AGAINST(#{keyword} IN BOOLEAN MODE)
OR cd.name LIKE CONCAT('%', #{keyword}, '%')
OR cd.summary LIKE CONCAT('%', #{keyword}, '%')
)
<if test="deptId != null">
AND cd.dept_id = #{deptId}
</if>
ORDER BY
CASE
WHEN MATCH(cd.name, cd.summary) AGAINST(#{keyword} IN BOOLEAN MODE) > 0 THEN 1
ELSE 2
END,
cd.update_time DESC
LIMIT #{offset}, #{pageSize}
</select>
<select id="globalSearchCount" resultType="java.lang.Long">
SELECT COUNT(*)
FROM cms_doc cd
WHERE
cd.is_dir = 0
AND cd.audit_status = '1'
AND (
MATCH(cd.name, cd.summary) AGAINST(#{keyword} IN BOOLEAN MODE)
OR cd.name LIKE CONCAT('%', #{keyword}, '%')
OR cd.summary LIKE CONCAT('%', #{keyword}, '%')
)
<if test="deptId != null">
AND cd.dept_id = #{deptId}
</if>
</select>
<!-- ResultMap for CmsDocWithScore -->
<resultMap type="com.microservices.cms.doc.domain.CmsDocWithScore" id="CmsDocWithScoreResult" extends="CmsDocResult">
<result property="searchScore" column="search_score"/>
</resultMap>
<!-- 全局搜索 - 带评分的全文索引查询 -->
<select id="globalSearchWithScore" resultMap="CmsDocWithScoreResult">
<include refid="selectCmsDocWithScoreVo"/>
WHERE
cd.is_dir = 0
AND cd.audit_status = '1'
AND (
MATCH(cd.name, cd.summary) AGAINST(#{keyword} IN BOOLEAN MODE)
OR cd.name LIKE CONCAT('%', #{keyword}, '%')
OR cd.summary LIKE CONCAT('%', #{keyword}, '%')
)
<if test="deptId != null">
AND cd.dept_id = #{deptId}
</if>
ORDER BY
-- 主要排序:按相关度评分降序
search_score DESC,
-- 次要排序:更新时间降序
cd.update_time DESC
LIMIT #{offset}, #{pageSize}
</select>
</mapper>

View File

@ -159,4 +159,38 @@
item="id" collection="array" open="(" separator="," close=")">#{id}
</foreach>
</delete>
<!-- 全局搜索 - 全文索引查询 -->
<select id="globalSearch" resultMap="CmsProjectResult">
SELECT id,
name,
description,
user_id,
dept_id,
identifier,
username,
create_by,
create_time,
update_time
FROM cms_project cp
WHERE
MATCH(cp.name) AGAINST(#{keyword} IN NATURAL LANGUAGE MODE)
<if test="deptId != null">
AND cp.dept_id = #{deptId}
</if>
ORDER BY
MATCH(cp.name) AGAINST(#{keyword} IN NATURAL LANGUAGE MODE) DESC,
cp.update_time DESC
LIMIT #{offset}, #{pageSize}
</select>
<select id="globalSearchCount" resultType="java.lang.Long">
SELECT COUNT(*)
FROM cms_project cp
WHERE
MATCH(cp.name) AGAINST(#{keyword} IN NATURAL LANGUAGE MODE)
<if test="deptId != null">
AND cp.dept_id = #{deptId}
</if>
</select>
</mapper>

View File

@ -1,6 +1,7 @@
package com.microservices.file.controller;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.file.service.ISysFileInfoService;
import com.microservices.system.api.domain.SysFileInfo;
@ -120,19 +121,20 @@ public class OpenController {
@ApiImplicitParam(name = "hierarchy", value = "层级结构eg: pms项目管理中{enterpriseIdentifier}/products,{enterpriseIdentifier}/projects,{enterpriseIdentifier}/docs/{projectId}", paramType = "query", dataType = "String")
})
public AjaxResult uploadFile(@RequestPart("file") MultipartFile file, String type, String hierarchy) {
SysFileInfo sysFileInfo = sysFileInfoService.upload(file, type, hierarchy);
AjaxResult ajax = AjaxResult.success();
ajax.put("url", sysFileInfo.getDownloadUrl());
if (sysFileInfo.getFileIdentifier() != null) {
ajax.put("fileIdentifier", sysFileInfo.getFileIdentifier());
}
ajax.put("fileName", sysFileInfo.getFilePath());
if (sysFileInfo.getFileId() != null) {
ajax.put("fileId", sysFileInfo.getFileId());
}
ajax.put("fileOriginName", sysFileInfo.getFileOriginName());
ajax.put("fileSuffix", sysFileInfo.getFileSuffix());
ajax.put("fileSize", sysFileInfo.getFileSizeInfo());
return ajax;
throw new ServiceException("该接口已弃用!");
// SysFileInfo sysFileInfo = sysFileInfoService.upload(file, type, hierarchy);
// AjaxResult ajax = AjaxResult.success();
// ajax.put("url", sysFileInfo.getDownloadUrl());
// if (sysFileInfo.getFileIdentifier() != null) {
// ajax.put("fileIdentifier", sysFileInfo.getFileIdentifier());
// }
// ajax.put("fileName", sysFileInfo.getFilePath());
// if (sysFileInfo.getFileId() != null) {
// ajax.put("fileId", sysFileInfo.getFileId());
// }
// ajax.put("fileOriginName", sysFileInfo.getFileOriginName());
// ajax.put("fileSuffix", sysFileInfo.getFileSuffix());
// ajax.put("fileSize", sysFileInfo.getFileSizeInfo());
// return ajax;
}
}

View File

@ -96,6 +96,11 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
@Autowired
private RedisService redisService;
/** 积分异步任务 Redis 队列 Key */
private static final String ZONE_POINTS_ASYNC_QUEUE_KEY = "zone:points:async:queue";
/** 资源下载积分任务 sourceType */
private static final String DOWNLOAD_RESOURCE_SOURCE_TYPE = "downloadResource";
@Override
public SysFileInfo selectSysFileInfoByFileObjectName(String fileObjectName) {
return sysFileInfoMapper.selectSysFileInfoByFileObjectName(fileObjectName);
@ -616,6 +621,15 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
sysFileInfo.setDownloadCount(downloadCount);
updateSysFileInfo(sysFileInfo);
// 触发资源下载积分任务直接写入 Redis 异步队列
try {
JSONObject task = new JSONObject();
task.put("sourceType", DOWNLOAD_RESOURCE_SOURCE_TYPE);
task.put("sourceId", sysFileInfo.getFileId());
redisService.leftPush(ZONE_POINTS_ASYNC_QUEUE_KEY, JSONObject.toJSONString(task));
} catch (Exception e) {
logger.warn("触发资源下载积分任务失败 fileId={} error={}", sysFileInfo.getFileId(), e.getMessage());
}
} catch (Exception e) {
throw new ServiceException("文件下载异常:%s", e.getMessage());
}

View File

@ -11,6 +11,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Size;
import java.util.List;
/**
@ -71,12 +72,18 @@ public class PmsProjectTestcaseDataVo extends BaseEntityVo {
@ApiModelProperty(value = "关联产品需求")
private PmsProductRequirement productReqSpecs;
@ApiModelProperty(value = "测试追踪")
private String testTracking;
/**
* 用例索引
*/
@ApiModelProperty(value = "用例索引")
private Long index;
@ApiModelProperty(value = "测试用例标识")
private String identifier;
public PmsProjectTestcaseDetailVo toPmsProjectTestcaseDetailVo() {
PmsProjectTestcaseDetailVo target = new PmsProjectTestcaseDetailVo();

View File

@ -25,7 +25,7 @@
<result property="testMethod" column="test_method"/>
<result property="terminationConditions" column="termination_conditions"/>
<result property="identifier" column="identifier"/>
<result property="testTracking" column="test_track"/>
<result property="testTracking" column="test_tracking"/>
<result property="productReqSpecsId" column="product_req_specs_id"/>
<result property="isImport" column="is_import"/>
</resultMap>
@ -33,6 +33,7 @@
<sql id="selectPmsProjectTestcaseVo">
select id,
title,
test_tracking,
assignee_gitlink_id,
pms_project_id,
type_id,
@ -186,7 +187,7 @@
<if test="testMethod != null">test_method = #{testMethod},</if>
<if test="terminationConditions != null">termination_conditions = #{terminationConditions},</if>
<if test="identifier != null">identifier = #{identifier},</if>
<if test="testTracking != null">#{testTracking},</if>
<if test="testTracking != null">test_tracking = #{testTracking},</if>
<if test="productReqSpecsId != null">#{productReqSpecsId},</if>
<if test="isImport != null">#{isImport},</if>
</trim>

View File

@ -1,5 +1,6 @@
package com.microservices.system.controller;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.StringUtils;
@ -203,6 +204,26 @@ public class SysUserController extends BaseController {
return R.ok(sysUser);
}
@InnerAuth
@GetMapping("/getSysUserListByUserIds")
R<List<SysUser>> getSysUserListByUserIds(@RequestParam("userIds") Long[] userIds) {
List<SysUser> sysUserList = userService.selectUserListFromUserIds(userIds);
return R.ok(sysUserList);
}
/**
* 通过用户ID数组批量获取用户信息POST方法解决URL长度限制
*
* @param userIds 用户ID数组
* @return 用户列表
*/
@InnerAuth
@PostMapping("/getSysUserListByUserIds")
R<List<SysUser>> postSysUserListByUserIds(@RequestBody Long[] userIds) {
List<SysUser> sysUserList = userService.selectUserListFromUserIds(userIds);
return R.ok(sysUserList);
}
/**
* 通过GitLinkToken获取本系统Token
*

View File

@ -289,6 +289,24 @@ public class SysUserDeptRoleController extends BaseController {
return R.ok(userInDeptHasRole);
}
/**
* 检查用户是否在指定部门或父级部门下具有指定菜单权限
*
* @param deptId 组织id
* @param userId 用户id
* @param perms 菜单权限
* @return 用户是否指定部门或父级部门下具有菜单权限 是true 否false
*/
@ApiOperation(value = "检查用户是否在指定部门或父级部门下具有指定菜单权限", hidden = true)
@InnerAuth
@GetMapping("/checkUserInDeptOrParentDeptHasPerms")
public R<Boolean> checkUserInDeptOrParentDeptHasPerms(@ApiParam(name = "deptId", value = "组织id", required = true) Long deptId,
@ApiParam(name = "userId", value = "用户id", required = true) Long userId,
@ApiParam(name = "perms", value = "菜单权限", required = true) String perms) {
Boolean userInDeptHasRole = sysUserDeptRoleService.checkUserInDeptOrParentDeptHasPerms(deptId, userId, perms);
return R.ok(userInDeptHasRole);
}
/**
* 检查用户是否在指定部门下
*
@ -304,4 +322,12 @@ public class SysUserDeptRoleController extends BaseController {
Boolean userInDept = sysUserDeptRoleService.checkUserInDept(currentDeptId, userId);
return R.ok(userInDept);
}
@ApiOperation(value = "用户是否在指定部门下", hidden = true)
@InnerAuth
@GetMapping("/getSysUserDeptRoleByDeptIdAndUserId")
public R<SysUserDeptRole> getSysUserDeptRoleByDeptIdAndUserId(@ApiParam(name = "deptId", value = "组织id", required = true) Long deptId,
@ApiParam(name = "userId", value = "用户id", required = true) Long userId) {
SysUserDeptRole userIdentifier = sysUserDeptRoleService.getSysUserDeptRoleByDeptIdAndUserId(deptId, userId);
return R.ok(userIdentifier);
}
}

View File

@ -151,4 +151,6 @@ public interface SysUserDeptRoleMapper {
* @param state 状态
*/
void updateUserIdentifyStateByUserId(@Param("userId") Long userId, @Param("state") String state);
int selectUserInDeptOrParentDeptHasPermsCount(@Param("deptId") Long deptId, @Param("userId") Long userId, @Param("perms") String perms);
}

View File

@ -131,4 +131,6 @@ public interface SysUserMapper
public SysUser checkEmailUnique(String email);
SysUser selectUserByGitLinkUserId(Long gitLinkUserId);
List<SysUser> selectUserListFromUserIds(Long[] ids);
}

View File

@ -160,4 +160,8 @@ public interface ISysUserDeptRoleService {
* @param userId 用户Id
*/
void deleteSysUserDeptRoleByDeleteUser(@Param("userId") Long userId);
Boolean checkUserInDeptOrParentDeptHasPerms(Long deptId, Long userId, String perms);
SysUserDeptRole getSysUserDeptRoleByDeptIdAndUserId(Long deptId, Long userId);
}

View File

@ -222,4 +222,6 @@ public interface ISysUserService
String getUserNameByGitLinkToken(String gitLinkToken);
SysUser gitlinkUserToUpdateSysUser(SysUser sysUser, ForgeUser forgeUser);
List<SysUser> selectUserListFromUserIds(Long[] userIds);
}

View File

@ -83,8 +83,8 @@ public class SysUserDeptRoleServiceImpl implements ISysUserDeptRoleService {
* 查询用户与部门及部门内角色关联列表
*
* @param userName 用户名
* @param deptId 组织Id
* @param roleId 角色Id
* @param deptId 组织Id
* @param roleId 角色Id
* @return 用户与部门及部门内角色关联
*/
@Override
@ -124,7 +124,10 @@ public class SysUserDeptRoleServiceImpl implements ISysUserDeptRoleService {
}
for (Long userId : userIds) {
SysUserDeptRole userIdentify = sysUserDeptRoleMapper.selectSysUserDeptRoleByUserIdAndDeptId(userId, deptId);
updateUserGitLinkRole(userId, deptId, userIdentify.getRoleId(), null, OPERATE_REMOVE);
if (userIdentify != null) {
updateUserGitLinkRole(userId, deptId, userIdentify.getRoleId(), null, OPERATE_REMOVE);
}
}
int success = deleteUserIdentityByDeptIdAndUserIds(sysDept, userIds);
for (Long userId : userIds) {
@ -166,8 +169,11 @@ public class SysUserDeptRoleServiceImpl implements ISysUserDeptRoleService {
if (sysUser == null) {
return;
}
String redisKey = CacheConstants.getUserIdentifyListKey(sysUser.getUserName());
// 删除缓存中该用户身份列表信息
redisService.deleteObject(CacheConstants.getUserIdentifyListKey(sysUser.getUserName()));
redisService.deleteObject(CacheConstants.getUserIdentifyListKey(redisKey));
List<SysUserDeptRole> userIdentifyList = selectSysUserDeptRoleListByUserName(sysUser.getUserName());
redisService.setCacheObject(redisKey, userIdentifyList);
}
@ -231,8 +237,13 @@ public class SysUserDeptRoleServiceImpl implements ISysUserDeptRoleService {
if (secondaryDeptIds == null) {
return;
}
if (!secondaryDeptIds.contains(dept.getDeptId())) {
SysDept parentDept = sysDeptMapper.selectDeptById(dept.getParentId());
if (parentDept == null) {
return;
}
if (dept.getParentId() == 0L || (parentDept.getParentId() == 0L && dept.getDeptType() == CacheConstants.DEPT_TYPE_TSZQ)) {
remoteCmsService.changeUserOrgRole(userId, operate, SecurityConstants.INNER);
} else if (!secondaryDeptIds.contains(dept.getDeptId())) {
if (secondaryDeptIds.contains(dept.getParentId())) {
switch (dept.getDeptType()) {
case CacheConstants.DEPT_TYPE_TSZQ: {
@ -568,6 +579,17 @@ public class SysUserDeptRoleServiceImpl implements ISysUserDeptRoleService {
return false;
}
@Override
public Boolean checkUserInDeptOrParentDeptHasPerms(Long deptId, Long userId, String perms) {
int hasPermsCount = sysUserDeptRoleMapper.selectUserInDeptOrParentDeptHasPermsCount(deptId, userId, perms);
return hasPermsCount > 0;
}
@Override
public SysUserDeptRole getSysUserDeptRoleByDeptIdAndUserId(Long deptId, Long userId) {
return sysUserDeptRoleMapper.selectSysUserDeptRoleByUserIdAndDeptId(userId, deptId);
}
@Override
public Boolean checkUserInDept(Long currentDeptId, Long userId) {
SysUserDeptRole sysUserDeptRole = sysUserDeptRoleMapper.selectSysUserDeptRoleByUserIdAndDeptId(userId, currentDeptId);

View File

@ -37,10 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -684,4 +681,9 @@ public class SysUserServiceImpl implements ISysUserService {
return sysUser;
}
@Override
public List<SysUser> selectUserListFromUserIds(Long[] userIds) {
return userMapper.selectUserListFromUserIds(userIds);
}
}

View File

@ -121,19 +121,24 @@ public class SyncGitLinkUserInfoJob {
for (ForgeUserAction forgeUserAction : forgeUserActionList) {
String userActionDataBank = forgeUserAction.getDataBank();
if (StringUtils.isNotEmpty(userActionDataBank)) {
JSONObject jsonObject = JSON.parseObject(userActionDataBank);
if (jsonObject != null) {
Long userId = jsonObject.getLong("id");
if (userId != null) {
SysUser sysUser = sysUserMapper.selectUserByGitLinkUserId(forgeUserAction.getUserId());
if (sysUser != null && UserConstants.DEL_FLAG_FALSE.equals(sysUser.getDelFlag())) {
log.info("检测到用户{}在Forge端已注销将该用户在微服务平台的所有身份标记为已注销同时逻辑删除该用户", sysUser.getUserName());
// 更新用户所有身份为已注销
sysUserDeptRoleService.deleteSysUserDeptRoleByDeleteUser(sysUser.getUserId());
// 逻辑删除该用户
sysUserService.deleteUserById(sysUser.getUserId());
try {
JSONObject jsonObject = JSON.parseObject(userActionDataBank);
if (jsonObject != null) {
Long userId = jsonObject.getLong("id");
if (userId != null) {
SysUser sysUser = sysUserMapper.selectUserByGitLinkUserId(forgeUserAction.getUserId());
if (sysUser != null && UserConstants.DEL_FLAG_FALSE.equals(sysUser.getDelFlag())) {
log.info("检测到用户{}在Forge端已注销将该用户在微服务平台的所有身份标记为已注销同时逻辑删除该用户", sysUser.getUserName());
// 更新用户所有身份为已注销
sysUserDeptRoleService.deleteSysUserDeptRoleByDeleteUser(sysUser.getUserId());
// 逻辑删除该用户
sysUserService.deleteUserById(sysUser.getUserId());
}
}
}
} catch (Exception e) {
log.error("解析Forge用户操作JSON失败forgeUserActionId: {}, userId: {}, dataBank: {}, 错误信息: {}",
forgeUserAction.getId(), forgeUserAction.getUserId(), userActionDataBank, e.getMessage());
}
}
}

View File

@ -234,6 +234,18 @@
and udr.dept_id = #{deptId}
</where>
</select>
<select id="selectUserInDeptOrParentDeptHasPermsCount" resultType="java.lang.Integer">
SELECT count(DISTINCT sm.menu_id)
FROM sys_menu sm
INNER JOIN sys_role_menu srm ON sm.menu_id = srm.menu_id
INNER JOIN sys_user_dept_role sudr ON srm.role_id = sudr.role_id
CROSS JOIN (SELECT CONCAT(ancestors, ',', dept_id) AS dept_ancestors
FROM sys_dept
WHERE dept_id = #{deptId}) vars
WHERE sudr.user_id = #{userId}
AND FIND_IN_SET(sudr.dept_id, vars.dept_ancestors)
AND sm.perms = #{perms};
</select>
<insert id="insertSysUserDeptRole" parameterType="com.microservices.system.api.domain.SysUserDeptRole">
insert into sys_user_dept_role

View File

@ -200,8 +200,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where u.gitlink_user_id = #{gitLinkUserId}
limit 1
</select>
<select id="selectUserListFromUserIds" resultMap="SysUserResult">
<include refid="selectOnlyUserVo"/>
where user_id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user(
<if test="userId != null and userId != 0">user_id,</if>
<if test="gitlinkUserId != null and gitlinkUserId != 0">gitlink_user_id,</if>

View File

@ -54,6 +54,12 @@
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Microservices Common Redis -->
<dependency>
<groupId>com.microservices</groupId>
<artifactId>microservices-common-redis</artifactId>
</dependency>
<!-- Microservices Common Async -->
<dependency>
<groupId>com.microservices</groupId>

View File

@ -1,5 +1,6 @@
package com.microservices.zone.common.controller;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
@ -10,7 +11,12 @@ import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.zone.detail.domain.vo.ZoneFrontRoleVo;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.project.domain.vo.*;
import com.microservices.zone.project.service.IZoneProjectService;
import com.microservices.zone.member.service.IZonePointsAccountService;
import com.microservices.zone.project.service.IZoneProjectTypeService;
import com.microservices.zone.resource.domain.ZoneResource;
import com.microservices.zone.utils.ZoneConstants;
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
import com.microservices.zone.resource.domain.vo.ZoneResourceInputVo;
import com.microservices.zone.resource.domain.vo.ZoneResourceSearchVo;
@ -18,9 +24,14 @@ import com.microservices.zone.resource.domain.vo.ZoneResourceUpdateVo;
import com.microservices.zone.resource.service.IZoneResourceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
/**
* @author otto
*/
@ -32,9 +43,18 @@ public class FrontController extends BaseController {
@Autowired
private IZoneResourceService zoneResourceService;
@Autowired
private IZoneProjectService zoneProjectService;
@Autowired
private IZoneDetailService zoneDetailService;
@Autowired
private IZoneProjectTypeService zoneProjectTypeService;
@Autowired
private IZonePointsAccountService zonePointsAccountService;
/**
* 查询我的资源列表
*/
@ -91,6 +111,72 @@ public class FrontController extends BaseController {
return toAjax(zoneResourceService.deleteMyZoneResourceByIds(ids));
}
/**
* 查询GitLink项目列表
*/
@GetMapping("/zone/{zoneId}/myGitlinkProjectList")
@ApiOperation("查询专区下我的GitLink项目列表")
public GenericsTableDataInfo<JSONObject> selectMyGitlinkProjectListByZone(
@ApiParam(name = "zoneId", value = "专区Id") @PathVariable Long zoneId
, MyGitLinkProjectSearchVo myGitLinkProjectSearchVo) {
return zoneProjectService.selectMyGitlinkProjectListByZone(zoneId, myGitLinkProjectSearchVo);
}
/**
* 查询我的项目列表
*/
@RequiresPermissions("zone:front:project:myList")
@GetMapping("/myProjectList")
@ApiOperation(value = "查询我的项目聚合列表")
public GenericsTableDataInfo<ZoneProjectDataVo> myProjectList(ZoneProjectSearchVo zoneProjectSearchVo) {
zoneProjectSearchVo.setCreateBy(SecurityUtils.getUsername());
return zoneProjectService.selectMyZoneProjectList(zoneProjectSearchVo);
}
/**
* 查询我的项目详情
*/
@RequiresPermissions("zone:front:project:query")
@GetMapping("/myProject/{id}")
@ApiOperation(value = "查询我的项目聚合详情")
public GenericsAjaxResult<ZoneProjectDataVo> myProject(@PathVariable("id") Long id) {
return genericsSuccess(zoneProjectService.selectZoneProjectDataById(id));
}
/**
* 新增项目
*/
@RequiresPermissions("zone:front:project:add")
@Log(title = "专区前台项目操作", businessType = BusinessType.INSERT)
@PostMapping("/addProject")
@ApiOperation(value = "新增项目聚合")
public AjaxResult addProject(@RequestBody @Validated List<ZoneProjectInputVo> zoneProjectInputVoList) {
return success(zoneProjectService.insertBatchZoneProject(zoneProjectInputVoList));
}
/**
* 编辑项目
*/
@RequiresPermissions("zone:front:project:edit")
@Log(title = "专区前台项目操作", businessType = BusinessType.UPDATE)
@PutMapping("/editProject/{projectId}")
@ApiOperation(value = "编辑项目聚合")
public AjaxResult editProject(@PathVariable("projectId") Long projectId,@RequestBody ZoneProjectFrontUpdateVo zoneProjectFrontUpdateVo) {
return success(zoneProjectService.updateMyZoneProject(projectId, zoneProjectFrontUpdateVo.toZoneProjectUpdateVo()));
}
/**
* 删除项目
*/
@RequiresPermissions("zone:front:project:remove")
@Log(title = "专区前台项目操作", businessType = BusinessType.DELETE)
@DeleteMapping("/removeProject/{ids}")
@ApiOperation(value = "删除项目聚合")
public AjaxResult removeProject(@PathVariable Long[] ids) {
return toAjax(zoneProjectService.deleteMyZoneProjectByIds(ids));
}
/**
* 检查当前登录用户在当前专区下角色
*/
@ -101,6 +187,11 @@ public class FrontController extends BaseController {
"3、当权限为None时不展示前台操作按钮\n" +
"4、检测到用户登录后才能调用该接口否则不展示前台操作按钮")
public GenericsAjaxResult<ZoneFrontRoleVo> checkCurrentRole(@PathVariable Long zoneId) {
return genericsSuccess(zoneDetailService.checkCurrentRoleInZone(zoneId));
ZoneFrontRoleVo zoneFrontRoleVo = zoneDetailService.checkCurrentRoleInZone(zoneId);
// 专区普通会员触发每日首次访问积分+1
if (ZoneConstants.FRONT_ROLE_MEMBER.equals(zoneFrontRoleVo.getRole())) {
zonePointsAccountService.addDailyVisitPointsAsync(zoneId);
}
return genericsSuccess(zoneFrontRoleVo);
}
}

View File

@ -1,6 +1,7 @@
package com.microservices.zone.common.controller;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.PageHelper;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
@ -19,7 +20,12 @@ import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.member.domain.vo.ZoneMemberDataVo;
import com.microservices.zone.member.domain.vo.ZoneMemberOpenSearchVo;
import com.microservices.zone.member.domain.vo.ZoneMemberOverviewVo;
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
import com.microservices.zone.member.domain.vo.ZonePointsTopDocVo;
import com.microservices.zone.member.service.IZoneMemberService;
import com.microservices.zone.member.service.IZoneMemberTypeService;
import com.microservices.zone.member.service.IZonePointsAccountService;
import com.microservices.zone.partners.domain.vo.ZonePartnersOverviewVo;
import com.microservices.zone.partners.service.IZonePartnersService;
import com.microservices.zone.project.domain.ZoneProjectType;
@ -29,7 +35,6 @@ import com.microservices.zone.project.domain.vo.ZoneProjectSearchVo;
import com.microservices.zone.project.domain.vo.ZoneStatisticsVo;
import com.microservices.zone.project.service.IZoneProjectService;
import com.microservices.zone.project.service.IZoneProjectTypeService;
import com.microservices.zone.resource.domain.ZoneResource;
import com.microservices.zone.resource.domain.ZoneResourceDomain;
import com.microservices.zone.resource.domain.ZoneResourceType;
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
@ -37,6 +42,10 @@ import com.microservices.zone.resource.domain.vo.ZoneResourceOpenSearchVo;
import com.microservices.zone.resource.service.IZoneResourceDomainService;
import com.microservices.zone.resource.service.IZoneResourceService;
import com.microservices.zone.resource.service.IZoneResourceTypeService;
import com.microservices.zone.search.dto.GlobalSearchRequest;
import com.microservices.zone.search.dto.GlobalSearchResponse;
import com.microservices.zone.search.dto.GlobalSearchResult;
import com.microservices.zone.search.service.IGlobalSearchService;
import com.microservices.zone.specialProject.domain.ZoneSpecialProject;
import com.microservices.zone.specialProject.domain.ZoneSpecialProjectRelevancy;
import com.microservices.zone.specialProject.domain.ZoneSpecialProjectReport;
@ -54,6 +63,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
/**
@ -69,6 +79,8 @@ public class OpenController extends BaseController {
@Autowired
private IZoneMemberService zoneMemberService;
@Autowired
private IZoneMemberTypeService zoneMemberTypeService;
@Autowired
private IZonePartnersService zonePartnersService;
@Autowired
private IZoneProjectService zoneProjectService;
@ -77,6 +89,8 @@ public class OpenController extends BaseController {
@Autowired
private IZoneDetailService zoneDetailService;
@Autowired
private IZonePointsAccountService zonePointsAccountService;
@Autowired
private IZoneResourceService zoneResourceService;
@Autowired
private IZoneDataSetService zoneDataSetService;
@ -211,6 +225,27 @@ public class OpenController extends BaseController {
return genericsSuccess(zoneDetailService.getZoneStatistics(zoneId));
}
/**
* 查询专区置顶文章列表含首页推荐文章去重置顶在前
*/
@GetMapping("/getTopDoc/{zoneId}")
@ApiOperation(value = "查询专区置顶文章列表(含首页推荐,去重)")
public GenericsAjaxResult<List<ZonePointsTopDocVo>> getTopDoc(@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsAccountService.selectHomePageDocMergeList(zoneId));
}
/**
* 查询专区置顶项目列表
*/
@GetMapping("/getTopProject/{zoneId}")
@ApiOperation(value = "查询专区置顶项目列表")
public GenericsAjaxResult<List<ZoneProjectDataVo>> getTopProject(@PathVariable("zoneId") Long zoneId) {
if (!zoneDetailService.isPointsZone(zoneId)) {
return genericsSuccess(Collections.emptyList());
}
return genericsSuccess(zoneProjectService.selectTopProjectList(zoneId));
}
/**
* 查询特色专区项目分类列表
*/
@ -241,10 +276,45 @@ public class OpenController extends BaseController {
@GetMapping("/{zoneId}/member/homePageList")
@ApiOperation("获取首页特色专区会员列表")
public GenericsTableDataInfo<ZoneMemberDataVo> homePageMemberList(@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId) {
List<ZoneMemberDataVo> list = zoneMemberService.selectZoneMemberHomePageMemberList(zoneId,true);
List<ZoneMemberDataVo> list = zoneMemberService.selectZoneMemberHomePageMemberList(zoneId, true);
return getGenericsDataTable(list);
}
/**
* 获取特色专区会员分类列表含各分类下已审核会员数量
*/
@GetMapping("/{zoneId}/memberType/list")
@ApiOperation("获取特色专区会员分类列表(含已审核会员数量)")
public GenericsTableDataInfo<ZoneMemberTypeCountVo> memberTypeList(
@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId,
@ApiParam(name = "name", value = "会员分类名称") String name) {
List<ZoneMemberTypeCountVo> list = zoneMemberTypeService.selectZoneMemberTypeListWithCount(zoneId, name);
return getGenericsDataTable(list);
}
/**
* 获取特色专区会员列表仅已审核会员支持名称模糊搜索及分类/等级过滤
*/
@GetMapping("/{zoneId}/member/list")
@ApiOperation("获取特色专区会员列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNum", value = "当前页码", paramType = "query", dataType = "Integer"),
@ApiImplicitParam(name = "pageSize", value = "每页显示记录数未传默认100", paramType = "query", dataType = "Integer")
})
public GenericsTableDataInfo<ZoneMemberDataVo> memberList(
@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId,
ZoneMemberSearchVo zoneMemberSearchVo,
@RequestParam(value = "pageNum", required = false) Integer pageNum,
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
zoneMemberSearchVo.setZoneId(zoneId);
if (pageNum == null || pageNum < 1) {
pageNum = 1;
}
int effectivePageSize = (pageSize == null || pageSize < 1) ? 100 : pageSize;
PageHelper.startPage(pageNum, effectivePageSize);
return zoneMemberService.selectOpenZoneMemberList(zoneMemberSearchVo);
}
/**
* 检查当前用户是否有该专区的权限
*/
@ -411,4 +481,20 @@ public class OpenController extends BaseController {
List<ZoneSpecialProjectReport> list = zoneSpecialProjectReportService.selectZoneSpecialProjectReportList(zoneSpecialProjectReportSearchVo.toZoneSpecialProjectReport());
return getGenericsDataTable(list);
}
@Autowired
private IGlobalSearchService globalSearchService;
/**
* 全局搜索接口POST方法
*
* @return 搜索结果
*/
@ApiOperation("全局搜索")
@PostMapping("/global")
public GenericsTableDataInfo<GlobalSearchResult> globalSearch(@RequestBody GlobalSearchRequest globalSearchRequest) {
logger.info("全局搜索请求: keyword={}, types={}", globalSearchRequest.getKeyword(), globalSearchRequest.getTypes());
GlobalSearchResponse response = globalSearchService.globalSearch(globalSearchRequest);
return new GenericsTableDataInfo<>(response.getResults(), response.getTotal());
}
}

View File

@ -1,9 +1,12 @@
package com.microservices.zone.common.service;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.zone.detail.domain.ZoneDetail;
import com.microservices.zone.project.domain.ZoneProject;
import com.microservices.zone.resource.domain.ZoneResourceType;
import java.util.List;
/**
* 特色专区异步Service接口
*
@ -51,4 +54,16 @@ public interface IZoneAsyncService {
void setProjectGitlinkInfoCache(ZoneProject zoneProject);
void clearZoneProjectSortByScoreCache(ZoneProject zoneProject);
List<JSONObject> getGitlinkProjectsDetail(List<String> gitLinkProjectIdList);
/**
* 异步发放项目审核通过积分
*
* @param zoneId 专区ID
* @param createBy 项目创建人用户名
* @param projectId 项目ID
* @param projectName 项目名称
*/
void addPublishProjectPointsAsync(Long zoneId, String createBy, Long projectId, String projectName);
}

View File

@ -1,5 +1,6 @@
package com.microservices.zone.common.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.CacheConstants;
@ -10,6 +11,7 @@ import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.redis.service.RedisService;
import com.microservices.system.api.RemoteCmsService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.utils.FeignUtils;
import com.microservices.zone.common.service.IZoneAsyncService;
import com.microservices.zone.detail.domain.ZoneDetail;
@ -60,12 +62,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
@ -80,6 +80,8 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
@Autowired
private RemoteCmsService remoteCmsService;
@Autowired
private RemoteUserService remoteUserService;
@Autowired
@Lazy
private IZoneProjectTypeService zoneProjectTypeService;
@Autowired
@ -142,6 +144,21 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
@Value("${zone.needCalcScoreZoneIds:}")
private String needCalcScoreZoneIds;
@Value("${zone.pointsDeptId:0}")
private Long pointsDeptId;
/**
* 项目启动时初始化积分部门ID到缓存
*/
@PostConstruct
public void initPointsDeptId() {
if (pointsDeptId != null && pointsDeptId > 0) {
redisService.setCacheObject(CacheConstants.ZONE_POINTS_DEPT_ID_KEY, pointsDeptId);
logger.info("积分部门ID初始化到缓存完成pointsDeptId={}", pointsDeptId);
}
}
/**
* 创建线程池
* 初始化专区耗时长在初始化文章经实验在文章初始化完成前其他步骤均已执行完成故设核心线程数为2
@ -153,11 +170,11 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
* Nthreads =Ncpu*Ucpu*(1+W/C)
*/
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2
5
, 10
, 10
, TimeUnit.SECONDS
, new ArrayBlockingQueue<>(100)
, new ArrayBlockingQueue<>(10000)
, Executors.defaultThreadFactory()
, (r, executor) -> {
throw new ServiceException("【%s】目前创建的人太多了请稍后再试", DateUtils.getNowDate());
@ -468,11 +485,6 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
for (ZoneProject zoneProject : zoneProjectList) {
setProjectGitlinkInfoCache(zoneProject);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
logger.error("初始化gitlink项目统计的定时任务延时被中断");
}
}
}
}
@ -522,6 +534,44 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
});
}
@Override
public List<JSONObject> getGitlinkProjectsDetail(List<String> gitLinkProjectIdList) {
int total = gitLinkProjectIdList.size();
if (total == 0) {
return new ArrayList<>();
}
// 向上取整分片修复原 size/50+1 在整除时多出一个空分片会发出一次 ids 为空的请求
int queryCount = (total + 49) / 50;
CountDownLatch countDownLatch = new CountDownLatch(queryCount);
// 使用线程安全的 ConcurrentLinkedQueue 收集结果修复原 ArrayList 在多线程并发 addAll 下丢数据/越界的隐患
ConcurrentLinkedQueue<JSONObject> gitLinkProjectQueue = new ConcurrentLinkedQueue<>();
for (int i = 0; i < queryCount; i++) {
int finalI = i;
threadPoolExecutor.execute(() -> {
try {
int pageMaxCount = Math.min((finalI + 1) * 50, total);
List<String> tmpIdList = gitLinkProjectIdList.subList(finalI * 50, pageMaxCount);
JSONObject result = gitLinkRequestHelper.doGet(
ZoneGitLinkRequestUrl.PROJECTS("?ids=" + String.join(ZoneConstants.ARRAY_SPLIT_SYMBOLS, tmpIdList))
);
gitLinkProjectQueue.addAll(result.getList(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY, JSONObject.class));
} catch (Exception e) {
logger.error("异步批量获取项目详细信息失败:{}", e.getMessage());
} finally {
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
logger.error("根据gitlinkProjectId调用Gitlink接口批量获取项目详细信息失败{}", e.getMessage());
throw new ServiceException("获取平台项目详细信息失败");
}
return new ArrayList<>(gitLinkProjectQueue);
}
private Double calcProjectScore(ZoneProject zoneProject, String startDate, String endDate) {
Double theWeekScore = forgeProjectStatisticsMapper
.selectLastWeekScoreByProjectId(zoneProject.getGitlinkProjectId(), startDate, endDate);
@ -537,4 +587,35 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
return theWeekScore;
}
@Async
@Override
public void addPublishProjectPointsAsync(Long zoneId, String createBy, Long projectId, String projectName) {
try {
if (zoneId == null || createBy == null) {
logger.warn("项目审核通过增加积分失败zoneId或createBy为空projectId={}", projectId);
return;
}
// 获取用户ID
com.microservices.system.api.domain.SysUser sysUser = FeignUtils.getReturnData(
remoteUserService.getSysUserByUserName(createBy, SecurityConstants.INNER));
if (sysUser == null || sysUser.getUserId() == null) {
logger.warn("项目审核通过增加积分失败未找到用户信息createBy={}projectId={}", createBy, projectId);
return;
}
// 写入 Redis 积分队列
JSONObject task = new JSONObject();
task.put("sourceType", ZoneConstants.PublishProject.SOURCE_TYPE);
task.put("userId", sysUser.getUserId());
task.put("zoneId", zoneId);
task.put("sourceId", projectId);
task.put("subjectDesc", projectName);
task.put("operatorId", sysUser.getUserId());
redisService.leftPush(ZoneConstants.ZONE_POINTS_ASYNC_QUEUE_KEY, JSON.toJSONString(task));
logger.info("项目审核通过增加积分任务已提交 userId={} projectId={} projectName={}", sysUser.getUserId(), projectId, projectName);
} catch (Exception e) {
logger.error("项目审核通过增加积分任务提交失败 projectId={} error={}", projectId, e.getMessage(), e);
}
}
}

View File

@ -190,6 +190,11 @@ public class ZoneDetail extends BaseEntity {
@ApiModelProperty(value = "资源是否需要审核0:不需要审核1需要审核")
private Integer resourceNeedAudit;
/**
* 项目是否需要审核0不需要1需要
*/
@ApiModelProperty(value = "项目是否需要审核0:不需要审核1需要审核")
private Integer projectNeedAudit;
/**
* 资讯是否锁定中0未锁定1锁定

View File

@ -155,6 +155,12 @@ public class ZoneDetailDataVo extends BaseEntity {
@ApiModelProperty(value = "资源是否需要审核0:不需要审核1需要审核")
private Integer resourceNeedAudit;
/**
* 项目是否需要审核0不需要1需要
*/
@ApiModelProperty(value = "项目是否需要审核0:不需要审核1需要审核")
private Integer projectNeedAudit;
/**
* 资讯是否锁定中0未锁定1锁定
*/
@ -180,6 +186,12 @@ public class ZoneDetailDataVo extends BaseEntity {
@ApiModelProperty(value = "会员板块是否展示0:不展示1展示")
private Integer sectionMemberShow;
/**
* 积分板块是否展示0:不展示1展示
*/
@ApiModelProperty(value = "积分板块是否展示0:不展示1展示")
private Integer sectionPointsShow;
/**
* 帮助中心板块是否展示0:不展示1展示
*/

View File

@ -123,6 +123,12 @@ public class ZoneDetailUpdateVo {
@ApiModelProperty(value = "资源是否需要审核0:不需要审核1需要审核")
private Integer resourceNeedAudit;
/**
* 项目是否需要审核0不需要1需要
*/
@ApiModelProperty(value = "项目是否需要审核0:不需要审核1需要审核")
private Integer projectNeedAudit;
/**
* 资讯是否锁定中0未锁定1锁定
*/

View File

@ -20,6 +20,12 @@ public class ZoneFrontRoleVo {
@ApiModelProperty(value = "资讯管理访问地址")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String docManageUrl;
@ApiModelProperty(value = "项目管理访问地址")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String projectManageUrl;
@ApiModelProperty(value = "用户访问组织Id")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Long deptId;
}

View File

@ -172,6 +172,7 @@ public interface IZoneDetailService {
ZoneFrontRoleVo checkCurrentRoleInZone(Long zoneId);
ZoneFrontRoleVo checkUserRoleInZone(Long zoneId, Long userId);
/**
* 通过组织id锁定/解锁专区资讯
*
@ -235,4 +236,20 @@ public interface IZoneDetailService {
ZoneDetail selectZoneDetailByIdNotVerifiedDataScope(Long zoneId);
ZoneStatisticsVo getZoneStatistics(Long zoneId);
/**
* 计算专区统计数据内部方法用于定时任务调用
*
* @param zoneId 专区ID
* @return 统计数据
*/
ZoneStatisticsVo calculateZoneStatistics(Long zoneId);
/**
* 判断专区是否为积分专区
*
* @param zoneId 专区ID
* @return true=是积分专区false=不是积分专区
*/
boolean isPointsZone(Long zoneId);
}

View File

@ -23,7 +23,9 @@ import com.microservices.system.api.RemoteDeptService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.domain.SysDept;
import com.microservices.system.api.domain.SysDictData;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.domain.SysUserDeptRole;
import com.microservices.system.api.model.LoginUser;
import com.microservices.system.api.utils.FeignUtils;
import com.microservices.zone.common.service.IZoneAsyncService;
import com.microservices.zone.detail.domain.ZoneDetail;
@ -31,10 +33,14 @@ import com.microservices.zone.detail.domain.vo.*;
import com.microservices.zone.detail.mapper.ZoneDetailMapper;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.member.domain.ZoneMember;
import com.microservices.zone.member.domain.ZoneMemberLevel;
import com.microservices.zone.member.domain.ZoneMemberType;
import com.microservices.zone.member.domain.vo.ZoneMemberInputVo;
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
import com.microservices.zone.member.mapper.ZoneMemberLevelMapper;
import com.microservices.zone.member.mapper.ZoneMemberMapper;
import com.microservices.zone.member.mapper.ZoneMemberTypeMapper;
import com.microservices.zone.member.service.IZoneMemberService;
import com.microservices.zone.partners.mapper.ZonePartnersMapper;
import com.microservices.zone.partners.mapper.ZonePartnersTypeMapper;
import com.microservices.zone.project.domain.ZoneProject;
@ -50,6 +56,7 @@ import com.microservices.zone.resource.mapper.ZoneResourceTypeMapper;
import com.microservices.zone.specialProject.domain.ZoneSpecialProject;
import com.microservices.zone.specialProject.service.IZoneSpecialProjectService;
import com.microservices.zone.utils.ZoneConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
@ -61,6 +68,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@ -70,6 +78,7 @@ import java.util.stream.Collectors;
* @date 2023-05-09
*/
@Service
@Slf4j
public class ZoneDetailServiceImpl implements IZoneDetailService {
@Autowired
private ZoneDetailMapper zoneDetailMapper;
@ -106,16 +115,32 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
@Autowired
@Lazy
private IZoneSpecialProjectService zoneSpecialProjectService;
@Autowired
@Lazy
private IZoneMemberService zoneMemberService;
@Value("${zone.zoneAdminUrl}")
private String zoneAdminUrl;
@Value("${zone.zoneConfigurationUrl}")
private String zoneConfigurationUrl;
@Value("${zone.zoneConfigurationPath:/zone/configuration}")
private String zoneConfigurationPath;
@Value("${zone.adminUrl}")
private String adminUrl;
@Value("${zone.resourceManagePath:/zone/resource/resource}")
private String resourceManagePath;
@Value("${zone.docManagePath:/zone/content/doc}")
private String docManagePath;
@Value("${zone.projectManagePath:/zone/project/list}")
private String projectManagePath;
@Value("${zone.resourceManageUrl}")
private String resourceManageUrl;
@Value("${zone.docManageUrl}")
private String docManageUrl;
@Value("${zone.autoMemberZoneIds:}")
private String autoMemberZoneIds;
@Value("${zone.autoMemberTypeName:}")
private String autoMemberTypeName;
@Value("${zone.autoMemberLevelName:}")
private String autoMemberLevelName;
@Autowired
private RedisService redisService;
@ -277,6 +302,16 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
throw new ServiceException("当前专区下存在待审核资源,需完成审核后才能将专区资源修改为不需要审核(专区名称[%s],待审核资源数量[%d])", oldZoneDetail.getName(), notAuditCount);
}
}
// 专区项目聚合审核设置由需要审核修改为不需要审核时需检查当前专区下是否存在待审核项目聚合如果存在需要处理完成后再修改配置
if (zoneDetail.getProjectNeedAudit() != null && (0 == zoneDetail.getProjectNeedAudit())) {
ZoneProjectSearchVo zoneProjectSearchVo = new ZoneProjectSearchVo();
zoneProjectSearchVo.setZoneId(zoneDetail.getId());
zoneProjectSearchVo.setAuditStatus(ZoneConstants.PROJECT_NOT_AUDIT);
Integer notAuditCount = zoneProjectMapper.selectZoneProjectListCount(zoneProjectSearchVo);
if (notAuditCount > 0) {
throw new ServiceException("当前专区下存在待审核项目聚合,需完成审核后才能将专区项目聚合修改为不需要审核(专区名称[%s],待审核项目聚合数量[%d])", oldZoneDetail.getName(), notAuditCount);
}
}
// 专区资讯审核设置由需要审核修改为不需要审核时需检查当前专区下是否存在待审核资讯如果存在需要处理完成后再修改配置
if (zoneDetail.getDocNeedAudit() != null && (0 == zoneDetail.getDocNeedAudit())) {
Long notAuditCount = FeignUtils.getReturnData(
@ -505,14 +540,14 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
.filter(x -> zoneDetail.getDeptId().equals(x.getDeptId()))
.findFirst().orElse(null);
if (userIdentify != null) {
return genChangeCurrentUserIdentityUrl(userIdentify, gitLinkToken, zoneId, notMemberParentUserIdentify);
return genChangeCurrentUserIdentityUrl(userIdentify, zoneId, notMemberParentUserIdentify);
}
// 当不存在当前专区对应的管理权限时查找在父级组织中是否存在身份
userIdentify = userIdentifyList.stream()
.filter(x -> deptAncestors.contains(String.valueOf(x.getDeptId())))
.findFirst().orElse(null);
if (userIdentify != null) {
return genChangeCurrentUserIdentityUrl(userIdentify, gitLinkToken, zoneId, notMemberParentUserIdentify);
return genChangeCurrentUserIdentityUrl(userIdentify, zoneId, notMemberParentUserIdentify);
}
}
return null;
@ -525,12 +560,11 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
* <a href="https://www.gitlink.org.cn/Gitlink/forgeplus/issues/3523">参照需求</a>
*
* @param userIdentify
* @param gitLinkToken
* @param zoneId
* @param notMemberParentUserIdentify
* @return
*/
private String genChangeCurrentUserIdentityUrl(SysUserDeptRole userIdentify, String gitLinkToken, Long zoneId, SysUserDeptRole notMemberParentUserIdentify) {
private String genChangeCurrentUserIdentityUrl(SysUserDeptRole userIdentify, Long zoneId, SysUserDeptRole notMemberParentUserIdentify) {
String roleKey = userIdentify.getSysRole().getRoleKey();
Long deptId = userIdentify.getDeptId();
if (SystemRole.ZONE_MEMBER.getRoleKey().equals(roleKey)) {
@ -541,10 +575,10 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
}
if (SystemRole.ZONE_ADMIN.getRoleKey().equals(roleKey)
|| SystemRole.ZONE_OPERATOR.getRoleKey().equals(roleKey)) {
return zoneConfigurationUrl + "?changeCurrentUserIdentity=" + deptId;
return adminUrl + zoneConfigurationPath + "?changeCurrentUserIdentity=" + deptId;
}
if (SystemRole.PLATFORM_ADMIN.getRoleKey().equals(roleKey)) {
return zoneConfigurationUrl + "/" + zoneId + "?changeCurrentUserIdentity=" + deptId;
return adminUrl + zoneConfigurationPath + "/" + zoneId + "?changeCurrentUserIdentity=" + deptId;
}
return null;
@ -599,9 +633,16 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
if (zoneDetail == null) {
throw new ServiceException(HttpStatus.NOT_FOUND, "该特色专区不存在或无权限访问(专区标识[" + zoneKey + "])");
}
// zoneTemplateCheck(template,zoneDetailDataVo);
return getZoneDetailDataVoAndCmsProject(zoneDetail);
ZoneDetailDataVo zoneDetailDataVo = getZoneDetailDataVoAndCmsProject(zoneDetail);
// 判断当前zone的deptId是否为redis中对应的ZONE_POINTS_DEPT_ID_KEY
Long cachedPointsDeptId = redisService.getCacheObject(CacheConstants.ZONE_POINTS_DEPT_ID_KEY);
if (zoneDetail.getDeptId() != null && cachedPointsDeptId != null
&& zoneDetail.getDeptId().equals(cachedPointsDeptId)) {
zoneDetailDataVo.setSectionPointsShow(1);
} else {
zoneDetailDataVo.setSectionPointsShow(0);
}
return zoneDetailDataVo;
}
@Override
@ -631,23 +672,175 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
return JSON.toJSONString(zoneDetail);
}
private ZoneMemberInputVo newZoneMemberInputVo(Long zoneId, SysUser sysUser) {
if (sysUser.getGitlinkUserId() == null) {
return null;
}
ZoneMemberInputVo zoneMemberInputVo = new ZoneMemberInputVo();
zoneMemberInputVo.setGitLinkUserId(sysUser.getGitlinkUserId());
zoneMemberInputVo.setName(sysUser.getNickName());
zoneMemberInputVo.setImageUrl(sysUser.getAvatar());
ZoneMemberType zoneMemberTypeSearch = new ZoneMemberType();
zoneMemberTypeSearch.setZoneId(zoneId);
List<ZoneMemberType> zoneMemberTypes = zoneMemberTypeMapper.selectZoneMemberTypeList(zoneMemberTypeSearch);
if (zoneMemberTypes == null || zoneMemberTypes.isEmpty()) {
return null;
}
Long zoneMemberTypeId = zoneMemberTypes.get(0).getId();
if (StringUtils.isNotEmpty(autoMemberTypeName)) {
zoneMemberTypeId = zoneMemberTypes.stream().filter(x -> autoMemberTypeName.equals(x.getName())).map(ZoneMemberType::getId).findFirst().orElse(zoneMemberTypeId);
}
zoneMemberInputVo.setMemberTypeId(zoneMemberTypeId);
ZoneMemberLevel zoneMemberLevelSearch = new ZoneMemberLevel();
zoneMemberLevelSearch.setZoneId(zoneId);
List<ZoneMemberLevel> zoneMemberLevels = zoneMemberLevelMapper.selectZoneMemberLevelList(zoneMemberLevelSearch);
if (zoneMemberLevels == null || zoneMemberLevels.isEmpty()) {
return null;
}
Long zoneMemberLevelId = zoneMemberLevels.get(0).getId();
if (StringUtils.isNotEmpty(autoMemberLevelName)) {
zoneMemberLevelId = zoneMemberLevels.stream().filter(x -> autoMemberLevelName.equals(x.getName())).map(ZoneMemberLevel::getId).findFirst().orElse(zoneMemberLevelId);
}
zoneMemberInputVo.setMemberLevelId(zoneMemberLevelId);
zoneMemberInputVo.setIsSendInvitation(false);
zoneMemberInputVo.setZoneId(zoneId);
return zoneMemberInputVo;
}
@Override
public ZoneFrontRoleVo checkCurrentRoleInZone(Long zoneId) {
ZoneFrontRoleVo zoneFrontRoleVo = new ZoneFrontRoleVo();
ZoneDetail zoneDetail = selectZoneDetailById(zoneId);
boolean isZoneAdmin = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_ADMIN.getRoleKey());
boolean isZoneOperator = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_OPERATOR.getRoleKey());
boolean isZoneMember = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_MEMBER.getRoleKey());
if (isZoneAdmin || isZoneOperator) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl);
zoneFrontRoleVo.setDocManageUrl(docManageUrl);
} else if (isZoneMember) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MEMBER);
} else {
LoginUser loginUser = SecurityUtils.getLoginUser();
// 当前专区为指定专区时用户将自动成为专区会员
if (StringUtils.isNotEmpty(autoMemberZoneIds)) {
String[] idArr = autoMemberZoneIds.split(",");
for (String s : idArr) {
if (s.equals(String.valueOf(zoneId))) {
ZoneMember zoneMember = zoneMemberMapper.selectZoneMemberByZoneIdAndUserId(zoneId, loginUser.getUserId());
if (zoneMember == null) {
ZoneMemberInputVo zoneMemberInputVo = newZoneMemberInputVo(zoneId, loginUser.getSysUser());
if (zoneMemberInputVo == null) {
break;
}
zoneMemberService.insertZoneMember(zoneMemberInputVo);
}
}
}
}
SysDept sysDept = FeignUtils.getReturnData(remoteDeptService.getDeptByDeptId(zoneDetail.getDeptId(), SecurityConstants.INNER));
List<SysUserDeptRole> userIdentifyList = FeignUtils.getReturnData(
remoteUserService.getSysUserDeptRoleListByUserName(loginUser.getUsername(), SecurityConstants.INNER)
);
if (sysDept == null || userIdentifyList == null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_NONE);
return zoneFrontRoleVo;
} else {
List<String> deptIdList = Arrays.asList(sysDept.getAncestors().split(","));
SysUserDeptRole parentDeptUserIdentify = userIdentifyList
.stream()
.filter(x -> deptIdList.contains(String.valueOf(x.getDeptId())))
.filter(x -> !x.getSysRole().getRoleKey().equals(SystemRole.ZONE_MEMBER.getRoleKey()))
.findFirst().orElse(null);
SysUserDeptRole currentUserIdentify = userIdentifyList.stream().filter(x -> x.getDeptId().equals(zoneDetail.getDeptId())).findFirst().orElse(null);
String resourceManageUrl = adminUrl + resourceManagePath + "?changeCurrentUserIdentity=";
String docManageUrl = adminUrl + docManagePath + "?changeCurrentUserIdentity=";
String projectManageUrl = adminUrl + projectManagePath + "?changeCurrentUserIdentity=";
if (currentUserIdentify != null) {
if (currentUserIdentify.getSysRole().getRoleKey().equals(SystemRole.ZONE_MEMBER.getRoleKey())) {
if (parentDeptUserIdentify != null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(parentDeptUserIdentify.getDeptId());
} else {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MEMBER);
zoneFrontRoleVo.setDeptId(currentUserIdentify.getDeptId());
}
} else {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(currentUserIdentify.getDeptId());
}
return zoneFrontRoleVo;
} else if (parentDeptUserIdentify != null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(parentDeptUserIdentify.getDeptId());
return zoneFrontRoleVo;
}
}
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_NONE);
return zoneFrontRoleVo;
}
public ZoneFrontRoleVo checkUserRoleInZone(Long zoneId, Long userId) {
ZoneFrontRoleVo zoneFrontRoleVo = new ZoneFrontRoleVo();
ZoneDetail zoneDetail = selectZoneDetailById(zoneId);
SysUser sysUser = FeignUtils.getReturnData(
remoteUserService.getSysUserByUserId(userId, SecurityConstants.INNER)
);
if (sysUser == null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_NONE);
return zoneFrontRoleVo;
}
SysDept sysDept = FeignUtils.getReturnData(remoteDeptService.getDeptByDeptId(zoneDetail.getDeptId(), SecurityConstants.INNER));
List<SysUserDeptRole> userIdentifyList = FeignUtils.getReturnData(
remoteUserService.getSysUserDeptRoleListByUserName(sysUser.getUserName(), SecurityConstants.INNER)
);
if (sysDept == null || userIdentifyList == null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_NONE);
return zoneFrontRoleVo;
} else {
List<String> deptIdList = Arrays.asList(sysDept.getAncestors().split(","));
SysUserDeptRole parentDeptUserIdentify = userIdentifyList
.stream()
.filter(x -> deptIdList.contains(String.valueOf(x.getDeptId())))
.filter(x -> !x.getSysRole().getRoleKey().equals(SystemRole.ZONE_MEMBER.getRoleKey()))
.findFirst().orElse(null);
SysUserDeptRole currentUserIdentify = userIdentifyList.stream().filter(x -> x.getDeptId().equals(zoneDetail.getDeptId())).findFirst().orElse(null);
String resourceManageUrl = adminUrl + resourceManagePath + "?changeCurrentUserIdentity=";
String docManageUrl = adminUrl + docManagePath + "?changeCurrentUserIdentity=";
String projectManageUrl = adminUrl + projectManagePath + "?changeCurrentUserIdentity=";
if (currentUserIdentify != null) {
if (currentUserIdentify.getSysRole().getRoleKey().equals(SystemRole.ZONE_MEMBER.getRoleKey())) {
if (parentDeptUserIdentify != null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(parentDeptUserIdentify.getDeptId());
} else {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MEMBER);
zoneFrontRoleVo.setDeptId(currentUserIdentify.getDeptId());
}
} else {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + currentUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(currentUserIdentify.getDeptId());
}
return zoneFrontRoleVo;
} else if (parentDeptUserIdentify != null) {
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_MANAGER);
zoneFrontRoleVo.setResourceManageUrl(resourceManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDocManageUrl(docManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setProjectManageUrl(projectManageUrl + parentDeptUserIdentify.getDeptId());
zoneFrontRoleVo.setDeptId(parentDeptUserIdentify.getDeptId());
return zoneFrontRoleVo;
}
}
zoneFrontRoleVo.setRole(ZoneConstants.FRONT_ROLE_NONE);
return zoneFrontRoleVo;
}
@ -678,14 +871,14 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
}
if (SystemRole.ZONE_ADMIN.getRoleKey().equals(roleKey)
|| SystemRole.ZONE_OPERATOR.getRoleKey().equals(roleKey)) {
return zoneConfigurationUrl;
return adminUrl + zoneConfigurationPath;
}
if (SystemRole.PLATFORM_ADMIN.getRoleKey().equals(roleKey)) {
ZoneDetail zoneDetail = zoneDetailMapper.selectZoneDetailByDeptId(deptId);
if (zoneDetail == null) {
return zoneAdminUrl;
}
return zoneConfigurationUrl + "/" + zoneDetail.getId();
return adminUrl + zoneConfigurationPath + "/" + zoneDetail.getId();
}
return adminUrl;
@ -727,7 +920,7 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
}
@Override
public ZoneStatisticsVo getZoneStatistics(Long zoneId) {
public ZoneStatisticsVo calculateZoneStatistics(Long zoneId) {
ZoneStatisticsVo zoneStatisticsVo = new ZoneStatisticsVo();
ZoneDetail zoneDetail = zoneDetailMapper.selectZoneDetailById(zoneId);
@ -792,6 +985,45 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
return zoneStatisticsVo;
}
@Override
public ZoneStatisticsVo getZoneStatistics(Long zoneId) {
String cacheKey = CacheConstants.getZoneStatisticsKey(zoneId);
try {
// 1. 尝试从Redis缓存获取
ZoneStatisticsVo cachedStatistics = redisService.getCacheObject(cacheKey);
if (cachedStatistics != null) {
log.debug("专区[{}]统计数据来自缓存", zoneId);
return cachedStatistics;
}
} catch (Exception e) {
log.warn("专区[{}]统计数据缓存读取失败,执行降级逻辑", zoneId, e);
}
// 2. 缓存异常或未命中执行实时计算
log.info("专区[{}]统计数据缓存未命中,执行实时计算", zoneId);
ZoneStatisticsVo statistics = calculateZoneStatistics(zoneId);
// 3. 尝试缓存计算结果失败不影响主流程
try {
redisService.setCacheObject(cacheKey, statistics, 7200L, TimeUnit.SECONDS);
} catch (Exception e) {
log.warn("专区[{}]统计数据缓存写入失败", zoneId, e);
}
return statistics;
}
@Override
public boolean isPointsZone(Long zoneId) {
ZoneDetail zoneDetail = zoneDetailMapper.selectZoneDetailById(zoneId);
if (zoneDetail == null || zoneDetail.getDeptId() == null) {
return false;
}
Long cachedPointsDeptId = redisService.getCacheObject(CacheConstants.ZONE_POINTS_DEPT_ID_KEY);
return cachedPointsDeptId != null && zoneDetail.getDeptId().equals(cachedPointsDeptId);
}
public Date[] getLastWeek() {
LocalDate today = LocalDate.now();
LocalDate startOfWeek = today.with(java.time.DayOfWeek.MONDAY).minusDays(7);

View File

@ -67,6 +67,16 @@ public class ZoneMemberController extends BaseController {
return AjaxResult.success("审核状态获取成功",auditStatus);
}
/**
* 查询当前登录用户会员审核状态
*/
@GetMapping("/zone/{zoneId}/memberInfo")
@ApiOperation("查询当前登录用户会员信息")
public AjaxResult getMemberInfo(@PathVariable("zoneId") Long zoneId) {
ZoneMember memberInfo = zoneMemberService.selectZoneMemberInfo(zoneId);
return AjaxResult.success("审核状态获取成功",memberInfo);
}
/**
* 查询GitLink用户列表
*/
@ -108,6 +118,16 @@ public class ZoneMemberController extends BaseController {
return genericsSuccess(zoneMemberService.selectZoneMemberById(id));
}
/**
* 根据专区ID修改当前登录用户会员信息
*/
@PutMapping(value = "/customEdit/zone/{zoneId}")
@ApiOperation("根据专区ID修改当前登录用户会员信息")
@Log(title = "特色专区会员", businessType = BusinessType.UPDATE)
public AjaxResult customEdit(@PathVariable("zoneId") Long zoneId, @RequestBody ZoneMemberUpdateVo zoneMemberUpdateVo) {
return toAjax(zoneMemberService.updateZoneMemberByMember(zoneId, zoneMemberUpdateVo));
}
/**
* 检查并获取组织下的会员列表
*/

View File

@ -0,0 +1,218 @@
package com.microservices.zone.member.controller;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import com.microservices.zone.member.domain.vo.*;
import com.microservices.zone.member.service.IZonePointsAccountService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 专区积分账户Controller
*
* @author otto
*/
@RestController
@RequestMapping("/points/account")
@Api(tags = "专区积分账户接口")
public class ZonePointsAccountController extends BaseController {
@Autowired
private IZonePointsAccountService zonePointsAccountService;
/**
* 查询专区积分账户列表
*/
@RequiresPermissions("zone:points:account:list")
@GetMapping("/list")
@ApiOperation("查询专区积分账户列表")
public GenericsTableDataInfo<ZonePointsAccount> list(ZonePointsAccountSearchVo searchVo) {
startPage();
List<ZonePointsAccount> list = zonePointsAccountService.selectZonePointsAccountList(searchVo.toZonePointsAccount());
return getGenericsDataTable(list);
}
/**
* 获取专区积分账户详细信息
*/
@RequiresPermissions("zone:points:account:query")
@GetMapping(value = "/{id}")
@ApiOperation(value = "获取专区积分账户详细信息")
public GenericsAjaxResult<ZonePointsAccount> getInfo(@PathVariable("id") Long id) {
return genericsSuccess(zonePointsAccountService.selectZonePointsAccountById(id));
}
/**
* 新增专区积分账户
*/
@RequiresPermissions("zone:points:account:add")
@Log(title = "专区积分账户", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation(value = "新增专区积分账户")
public AjaxResult add(@RequestBody ZonePointsAccountInputVo inputVo) {
return toAjax(zonePointsAccountService.insertZonePointsAccount(inputVo));
}
/**
* 修改专区积分账户
*/
@RequiresPermissions("zone:points:account:edit")
@Log(title = "专区积分账户", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation(value = "修改专区积分账户")
public AjaxResult edit(@RequestBody ZonePointsAccountUpdateVo updateVo) {
return toAjax(zonePointsAccountService.updateZonePointsAccount(updateVo));
}
/**
* 删除专区积分账户
*/
@RequiresPermissions("zone:points:account:remove")
@Log(title = "专区积分账户", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation(value = "删除专区积分账户")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(zonePointsAccountService.deleteZonePointsAccountByIds(ids));
}
/**
* 根据用户ID和专区ID查询积分账户
*/
@RequiresPermissions("zone:points:account:query")
@GetMapping("/user/{userId}/zone/{zoneId}")
@ApiOperation(value = "根据用户ID和专区ID查询积分账户")
public GenericsAjaxResult<ZonePointsAccount> getByUserIdAndZoneId(@PathVariable("userId") Long userId,
@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsAccountService.selectZonePointsAccountByUserIdAndZoneId(userId, zoneId));
}
/**
* 根据会员ID查询积分账户列表
*/
@RequiresPermissions("zone:points:account:query")
@GetMapping("/member/{memberId}")
@ApiOperation(value = "根据会员ID查询积分账户列表")
public GenericsAjaxResult<List<ZonePointsAccount>> getByMemberId(@PathVariable("memberId") Long memberId) {
return genericsSuccess(zonePointsAccountService.selectZonePointsAccountByMemberId(memberId));
}
/**
* 获取当前登录用户的积分账户总览信息
*/
@GetMapping("/overview/{zoneId}")
@ApiOperation(value = "获取当前登录用户的积分账户总览信息")
public GenericsAjaxResult<ZonePointsOverviewVo> getCurrentUserPointsOverview(@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsAccountService.getCurrentUserPointsOverview(zoneId));
}
/**
* 电子证书积分扣减前台用户操作业务层已做会员身份+积分专区+幂等+积分校验
*/
@PostMapping("/certificate/redeem")
@ApiOperation(value = "电子证书积分扣减")
public AjaxResult redeemCertificate(@RequestBody ZonePointsTransactionInputVo inputVo) {
zonePointsAccountService.deductPointsForCertificate(inputVo);
return success();
}
/**
* 文章置顶积分扣减前台用户操作业务层已做会员身份+积分专区+积分校验
* 用户使用当前登录用户
*/
@PostMapping("/article/top")
@ApiOperation(value = "文章置顶积分扣减")
public AjaxResult topArticle(@RequestBody ZonePointsArticleTopInputVo inputVo) {
zonePointsAccountService.deductPointsForArticleTop(inputVo.getZoneId(), inputVo.getId(), inputVo.getName());
return success();
}
/**
* 获取当前登录用户的置顶文章信息
*/
@GetMapping("/article/top/{zoneId}")
@ApiOperation(value = "获取当前登录用户的置顶文章信息")
public GenericsAjaxResult<ZonePointsTopDocVo> getCurrentUserPointsTopDoc(@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsAccountService.getCurrentUserPointsTopDoc(zoneId));
}
/**
* 获取当前登录用户的置顶项目信息
*/
@GetMapping("/project/top/{zoneId}")
@ApiOperation(value = "获取当前登录用户的置顶项目信息")
public GenericsAjaxResult<ZonePointsTopProjectVo> getZonePointsTopProject(@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsAccountService.getZonePointsTopProject(zoneId));
}
/**
* 项目置顶积分扣减前台用户操作业务层已做会员身份+积分专区+积分校验
* 用户使用当前登录用户
*/
@PostMapping("/project/top")
@ApiOperation(value = "项目置顶积分扣减")
public AjaxResult topProject(@RequestBody ZonePointsProjectTopInputVo inputVo) {
zonePointsAccountService.deductPointsForProjectTop(inputVo.getZoneId(), inputVo.getId(), inputVo.getName());
return success();
}
/**
* 线下活动积分冻结前台用户操作业务层已做会员身份+积分专区+幂等+积分校验
*/
@PostMapping("/offlineActivity/freeze")
@ApiOperation(value = "线下活动积分冻结")
public AjaxResult freezePointsForOfflineActivity(@RequestBody ZonePointsApplyInputVo inputVo) {
zonePointsAccountService.freezePointsForOfflineActivity(inputVo);
return success();
}
/**
* 专家答疑积分冻结前台用户操作业务层已做会员身份+积分专区+幂等+积分校验
*/
@PostMapping("/expert/qa/freeze")
@ApiOperation(value = "专家答疑积分冻结")
public AjaxResult freezePointsForExpertQa(@RequestBody ZonePointsApplyInputVo inputVo) {
zonePointsAccountService.freezePointsForExpertQa(inputVo);
return success();
}
/**
* 驳回积分申请管理员操作
* 查询对应申请记录pending状态更新状态为fail
* 同时更新pointsEvent的subject括号内容为"已退回"replyContent为驳回原因
* 并触发积分解冻操作
*/
@RequiresPermissions("zone:points:account:reject")
@Log(title = "驳回积分申请", businessType = BusinessType.UPDATE)
@PostMapping("/apply/reject")
@ApiOperation(value = "驳回积分申请")
public AjaxResult rejectPointsApply(@RequestBody ZonePointsApplyInputVo inputVo) {
zonePointsAccountService.rejectPointsApply(inputVo);
return success();
}
/**
* 通过积分申请管理员操作
* 查询对应申请记录pending状态更新状态为success
* 同时更新pointsEvent的subject去除原括号内容
* 并从冻结积分中正式扣减积分含总积分扣减
*/
@RequiresPermissions("zone:points:account:approve")
@Log(title = "通过积分申请", businessType = BusinessType.UPDATE)
@PostMapping("/apply/approve")
@ApiOperation(value = "通过积分申请")
public AjaxResult approvePointsApply(@RequestBody ZonePointsApplyInputVo inputVo) {
zonePointsAccountService.approvePointsApply(inputVo);
return success();
}
}

View File

@ -0,0 +1,111 @@
package com.microservices.zone.member.controller;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import com.microservices.zone.member.domain.vo.ZonePointsApplyDetailVo;
import com.microservices.zone.member.domain.vo.ZonePointsApplyInputVo;
import com.microservices.zone.member.domain.vo.ZonePointsApplySearchVo;
import com.microservices.zone.member.domain.vo.ZonePointsApplyUpdateVo;
import com.microservices.zone.member.service.IZonePointsApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 积分申请表Controller
*
* @author otto
*/
@RestController
@RequestMapping("/points/apply")
@Api(tags = "积分申请表接口")
public class ZonePointsApplyController extends BaseController {
@Autowired
private IZonePointsApplyService zonePointsApplyService;
/**
* 查询积分申请列表
*/
@RequiresPermissions("zone:points:apply:list")
@GetMapping("/list")
@ApiOperation("查询积分申请列表")
public GenericsTableDataInfo<ZonePointsApplyDetailVo> list(ZonePointsApplySearchVo searchVo) {
startPage();
return zonePointsApplyService.selectZonePointsApplyList(searchVo);
}
/**
* 查询我的积分申请列表
*/
@GetMapping("/my/list")
@ApiOperation("查询我的积分申请列表")
public GenericsTableDataInfo<ZonePointsApplyDetailVo> myList(ZonePointsApplySearchVo searchVo) {
startPage();
searchVo.setUserId(SecurityUtils.getUserId());
return zonePointsApplyService.selectZonePointsApplyList(searchVo);
}
/**
* 获取积分申请详细信息
*/
@RequiresPermissions("zone:points:apply:query")
@GetMapping(value = "/{id}")
@ApiOperation(value = "获取积分申请详细信息")
public GenericsAjaxResult<ZonePointsApply> getInfo(@PathVariable("id") Long id) {
return genericsSuccess(zonePointsApplyService.selectZonePointsApplyById(id));
}
/**
* 新增积分申请
*/
@RequiresPermissions("zone:points:apply:add")
@Log(title = "积分申请", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation(value = "新增积分申请")
public AjaxResult add(@RequestBody ZonePointsApplyInputVo inputVo) {
return toAjax(zonePointsApplyService.insertZonePointsApply(inputVo));
}
/**
* 修改积分申请
*/
@RequiresPermissions("zone:points:apply:edit")
@Log(title = "积分申请", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation(value = "修改积分申请")
public AjaxResult edit(@RequestBody ZonePointsApplyUpdateVo updateVo) {
return toAjax(zonePointsApplyService.updateZonePointsApply(updateVo));
}
/**
* 删除积分申请
*/
@RequiresPermissions("zone:points:apply:remove")
@Log(title = "积分申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation(value = "删除积分申请")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(zonePointsApplyService.deleteZonePointsApplyByIds(ids));
}
/**
* 根据用户ID和专区ID查询积分申请列表
*/
@RequiresPermissions("zone:points:apply:query")
@GetMapping("/user/{userId}/zone/{zoneId}")
@ApiOperation(value = "根据用户ID和专区ID查询积分申请列表")
public GenericsAjaxResult<List<ZonePointsApply>> getByUserIdAndZoneId(@PathVariable("userId") Long userId,
@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsApplyService.selectZonePointsApplyByUserIdAndZoneId(userId, zoneId));
}
}

View File

@ -0,0 +1,110 @@
package com.microservices.zone.member.controller;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.domain.GenericsAjaxResult;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import com.microservices.zone.member.domain.vo.ZonePointsTransactionInputVo;
import com.microservices.zone.member.domain.vo.ZonePointsTransactionSearchVo;
import com.microservices.zone.member.domain.vo.ZonePointsTransactionUpdateVo;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.zone.member.service.IZonePointsTransactionService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 专区积分流水Controller
*
* @author otto
*/
@RestController
@RequestMapping("/points/transaction")
@Api(tags = "专区积分流水接口")
public class ZonePointsTransactionController extends BaseController {
@Autowired
private IZonePointsTransactionService zonePointsTransactionService;
/**
* 查询专区积分流水列表
*/
@RequiresPermissions("zone:points:transaction:list")
@GetMapping("/list")
@ApiOperation("查询专区积分流水列表")
public GenericsTableDataInfo<ZonePointsTransaction> list(ZonePointsTransactionSearchVo searchVo) {
startPage();
List<ZonePointsTransaction> list = zonePointsTransactionService.selectZonePointsTransactionList(searchVo.toZonePointsTransaction());
return getGenericsDataTable(list);
}
/**
* 获取专区积分流水详细信息
*/
@RequiresPermissions("zone:points:transaction:query")
@GetMapping(value = "/{id}")
@ApiOperation(value = "获取专区积分流水详细信息")
public GenericsAjaxResult<ZonePointsTransaction> getInfo(@PathVariable("id") Long id) {
return genericsSuccess(zonePointsTransactionService.selectZonePointsTransactionById(id));
}
/**
* 根据事件ID查询流水列表
*/
@RequiresPermissions("zone:points:transaction:query")
@GetMapping("/event/{eventId}")
@ApiOperation(value = "根据事件ID查询流水列表")
public GenericsAjaxResult<List<ZonePointsTransaction>> getByEventId(@PathVariable("eventId") String eventId) {
return genericsSuccess(zonePointsTransactionService.selectZonePointsTransactionByEventId(eventId));
}
/**
* 根据账户ID查询流水列表
*/
@RequiresPermissions("zone:points:transaction:query")
@GetMapping("/account/{accountId}")
@ApiOperation(value = "根据账户ID查询流水列表")
public GenericsAjaxResult<List<ZonePointsTransaction>> getByAccountId(@PathVariable("accountId") Long accountId) {
return genericsSuccess(zonePointsTransactionService.selectZonePointsTransactionByAccountId(accountId));
}
/**
* 根据用户ID和专区ID查询流水列表
*/
@RequiresPermissions("zone:points:transaction:query")
@GetMapping("/user/{userId}/zone/{zoneId}")
@ApiOperation(value = "根据用户ID和专区ID查询流水列表")
public GenericsAjaxResult<List<ZonePointsTransaction>> getByUserIdAndZoneId(@PathVariable("userId") Long userId,
@PathVariable("zoneId") Long zoneId) {
return genericsSuccess(zonePointsTransactionService.selectZonePointsTransactionByUserIdAndZoneId(userId, zoneId));
}
/**
* 根据专区ID获取当前用户积分明细分页
*/
@GetMapping("/my/{zoneId}")
@ApiOperation(value = "根据专区ID获取当前用户积分明细分页")
public GenericsTableDataInfo<ZonePointsTransaction> getMyPointsDetailsByZone(@PathVariable("zoneId") Long zoneId,
ZonePointsTransactionSearchVo searchVo) {
startPage();
List<ZonePointsTransaction> list = zonePointsTransactionService.selectMyPointsTransactionDetailsByZone(zoneId, SecurityUtils.getUserId(), searchVo.getTransactionType());
return getGenericsDataTable(list);
}
/**
* 分页查询当前登录用户流水列表合并后所有业务字段已包含在流水中
*/
@GetMapping("/currentUser/{zoneId}")
@ApiOperation(value = "分页查询当前登录用户流水列表")
public GenericsTableDataInfo<ZonePointsTransaction> getCurrentUserTransactionList(@PathVariable("zoneId") Long zoneId) {
startPage();
List<ZonePointsTransaction> list = zonePointsTransactionService.selectCurrentUserTransactionWithEvent(SecurityUtils.getUserId(), zoneId);
return getGenericsDataTable(list);
}
}

View File

@ -5,10 +5,14 @@ import com.microservices.common.core.annotation.Excel;
import com.microservices.common.core.web.domain.BaseEntity;
import com.microservices.system.api.domain.SysDept;
import com.microservices.system.api.domain.SysUser;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import com.microservices.zone.member.domain.vo.ZoneMemberDataVo;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 特色专区会员对象 zone_member
@ -92,6 +96,18 @@ public class ZoneMember extends BaseEntity {
private SysUser sysUser;
private SysDept sysDept;
private ZoneMemberLevel zoneMemberLevel;
private ZonePointsAccount zonePointsAccount;
/**
* 积分流水合并后由 zone_points_transaction 表承载事件信息
*/
private ZonePointsTransaction pointsEvent;
/**
* 积分申请列表
*/
private List<ZonePointsApply> applyList;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date auditTime;

View File

@ -0,0 +1,26 @@
package com.microservices.zone.member.domain;
import com.microservices.zone.member.domain.ZoneMember;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* ZoneMember with full-text search score
* 用于全局搜索结果包含全文搜索相关度评分
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ZoneMemberWithScore extends ZoneMember implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 全文搜索相关度评分来自 MySQL MATCH...AGAINST
*/
private Double searchScore;
private String memberTypeName;
private String memberLevelName;
}

View File

@ -0,0 +1,68 @@
package com.microservices.zone.member.domain.points;
import com.microservices.common.core.annotation.Excel;
import com.microservices.common.core.web.domain.BaseEntity;
import lombok.Data;
/**
* 专区积分账户对象 zone_points_account
*
* @author otto
*/
@Data
public class ZonePointsAccount extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 积分账户主键
*/
private Long id;
/**
* 用户ID
*/
@Excel(name = "用户ID")
private Long userId;
/**
* 专区ID积分按专区隔离
*/
@Excel(name = "专区ID")
private Long zoneId;
/**
* 累计积分
*/
@Excel(name = "累计积分")
private Integer totalPoints;
/**
* 累计获取积分
*/
@Excel(name = "累计获取积分")
private Integer totalEarnedPoints;
/**
* 累计消费积分
*/
@Excel(name = "累计消费积分")
private Integer totalConsumedPoints;
/**
* 可用积分
*/
@Excel(name = "可用积分")
private Integer availablePoints;
/**
* 冻结积分
*/
@Excel(name = "冻结积分")
private Integer frozenPoints;
/**
* 所属组织ID
*/
@Excel(name = "所属组织ID")
private Long deptId;
}

View File

@ -0,0 +1,121 @@
package com.microservices.zone.member.domain.points;
import com.microservices.common.core.annotation.Excel;
import com.microservices.common.core.web.domain.BaseEntity;
import com.microservices.system.api.domain.SysUser;
import lombok.Data;
/**
* 积分申请表 zone_points_apply
*
* @author otto
*/
@Data
public class ZonePointsApply extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 用户ID
*/
@Excel(name = "用户ID")
private Long userId;
/**
* 专区ID
*/
@Excel(name = "专区ID")
private Long zoneId;
/**
* 组织ID
*/
@Excel(name = "组织ID")
private Long deptId;
/**
* 申请类型download下载/redeem兑换等
*/
@Excel(name = "申请类型")
private String applyType;
/**
* 事件ID关联积分流水事件
*/
@Excel(name = "事件ID")
private String eventId;
/**
* 回复/响应内容
*/
@Excel(name = "回复/响应内容")
private String reply;
/**
* 状态pending待处理/success成功/fail失败/rejected已拒绝
*/
@Excel(name = "状态")
private String status;
/**
* 申请积分数量
*/
@Excel(name = "申请积分数量")
private Integer points;
/**
* 回复人ID
*/
@Excel(name = "回复人ID")
private Long replyId;
/**
* 手机号
*/
@Excel(name = "手机号")
private String phone;
/**
* 真实姓名
*/
@Excel(name = "真实姓名")
private String realName;
/**
* 描述
*/
@Excel(name = "描述")
private String description;
/**
* 组织
*/
@Excel(name = "组织")
private String organization;
/**
* 微信号
*/
@Excel(name = "微信号")
private String wechat;
/**
* 邮箱
*/
@Excel(name = "邮箱")
private String email;
/**
* 专区成员名称
*/
private String memberName;
/**
* 系统用户信息
*/
private SysUser sysUser;
}

View File

@ -0,0 +1,56 @@
package com.microservices.zone.member.domain.points;
import com.microservices.common.core.web.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
/**
* 专区积分文档对象 zone_points_doc
*
* @author otto
*/
@Data
public class ZonePointsDoc extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 专区ID
*/
private Long zoneId;
/**
* 文章ID
*/
private Long docId;
/**
* 初始访问次数
*/
private Integer pointsStartVisits;
/**
* 已触发积分次数
*/
private Integer pointsTriggerCount;
/**
* 创建者
*/
private Long userId;
/**
* 过期时间
*/
private Date expiryTime;
/**
* 是否积分置顶0代表不置顶 1代表置顶
*/
private Boolean isPointsTop;
}

View File

@ -0,0 +1,51 @@
package com.microservices.zone.member.domain.points;
import com.microservices.common.core.web.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
/**
* 积分异步任务失败记录对象 zone_points_fail_log
*
* @author otto
*/
@Data
public class ZonePointsFailLog extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 任务完整JSON数据
*/
private String taskData;
/**
* 失败时间
*/
private Date failTime;
/**
* 最后失败原因
*/
private String lastError;
/**
* 处理状态
*/
private String status;
/**
* 处理时间
*/
private Date processTime;
/**
* 人工处理备注
*/
private String processRemark;
}

View File

@ -0,0 +1,104 @@
package com.microservices.zone.member.domain.points;
import com.microservices.common.core.annotation.Excel;
import com.microservices.common.core.web.domain.BaseEntity;
import lombok.Data;
/**
* 专区积分流水对象 zone_points_transaction合并事件+流水
*
* @author otto
*/
@Data
public class ZonePointsTransaction extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 流水主键
*/
private Long id;
/**
* 事件ID关联同一事件的所有操作
*/
@Excel(name = "事件ID")
private String eventId;
/**
* 事件类型
*/
@Excel(name = "事件类型")
private String eventType;
/**
* 积分账户ID
*/
@Excel(name = "积分账户ID")
private Long accountId;
/**
* 类型add增/reduce减/freezed冻结/unfreezed解冻
*/
@Excel(name = "类型")
private String transactionType;
/**
* 积分数量正数
*/
@Excel(name = "积分数量")
private Integer points;
/**
* 变动后可用积分
*/
@Excel(name = "变动后可用积分")
private Integer availableAfter;
/**
* 变动后冻结积分
*/
@Excel(name = "变动后冻结积分")
private Integer frozenAfter;
/**
* 操作人ID
*/
@Excel(name = "操作人ID")
private Long operatorId;
/**
* 变动原因
*/
@Excel(name = "变动原因")
private String reason;
/**
* 回复内容
*/
@Excel(name = "回复内容")
private String replyContent;
/**
* 来源ID
*/
@Excel(name = "来源ID")
private Long sourceId;
/**
* 来源数据
*/
@Excel(name = "来源数据")
private String sourceData;
/**
* 主题
*/
@Excel(name = "主题")
private String subject;
/**
* 主题描述
*/
@Excel(name = "主题描述")
private String subjectDesc;
}

View File

@ -0,0 +1,25 @@
package com.microservices.zone.member.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 专区会员分类含已审核会员数量对象
*
* @author otto
*/
@Data
@ApiModel("专区会员分类(含已审核会员数量)对象")
public class ZoneMemberTypeCountVo {
@ApiModelProperty(value = "专区会员分类Id")
private Long id;
@ApiModelProperty(value = "专区会员分类名称")
private String name;
@ApiModelProperty(value = "专区会员分类简介")
private String introduction;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "已审核会员数量")
private Long memberCount;
}

View File

@ -0,0 +1,52 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 积分账户输入对象
*
* @author otto
*/
@Data
@ApiModel("积分账户输入对象")
public class ZonePointsAccountInputVo {
@ApiModelProperty(value = "用户ID", required = true)
@NotNull
private Long userId;
@ApiModelProperty(value = "专区ID", required = true)
@NotNull
private Long zoneId;
@ApiModelProperty(value = "累计积分")
private Integer totalPoints;
@ApiModelProperty(value = "累计获取积分")
private Integer totalEarnedPoints;
@ApiModelProperty(value = "累计消费积分")
private Integer totalConsumedPoints;
@ApiModelProperty(value = "可用积分")
private Integer availablePoints;
@ApiModelProperty(value = "冻结积分")
private Integer frozenPoints;
public ZonePointsAccount toZonePointsAccount() {
ZonePointsAccount zonePointsAccount = new ZonePointsAccount();
zonePointsAccount.setUserId(userId);
zonePointsAccount.setZoneId(zoneId);
zonePointsAccount.setTotalPoints(totalPoints != null ? totalPoints : 0);
zonePointsAccount.setTotalEarnedPoints(totalEarnedPoints != null ? totalEarnedPoints : 0);
zonePointsAccount.setTotalConsumedPoints(totalConsumedPoints != null ? totalConsumedPoints : 0);
zonePointsAccount.setAvailablePoints(availablePoints != null ? availablePoints : 0);
zonePointsAccount.setFrozenPoints(frozenPoints != null ? frozenPoints : 0);
return zonePointsAccount;
}
}

View File

@ -0,0 +1,28 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 积分账户搜索对象
*
* @author otto
*/
@Data
@ApiModel("积分账户搜索对象")
public class ZonePointsAccountSearchVo {
@ApiModelProperty(value = "用户ID")
private Long userId;
@ApiModelProperty(value = "专区ID")
private Long zoneId;
public ZonePointsAccount toZonePointsAccount() {
ZonePointsAccount zonePointsAccount = new ZonePointsAccount();
zonePointsAccount.setUserId(userId);
zonePointsAccount.setZoneId(zoneId);
return zonePointsAccount;
}
}

View File

@ -0,0 +1,48 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 积分账户更新对象
*
* @author otto
*/
@Data
@ApiModel("积分账户更新对象")
public class ZonePointsAccountUpdateVo {
@ApiModelProperty(value = "积分账户ID", required = true)
@NotNull
private Long id;
@ApiModelProperty(value = "累计积分")
private int totalPoints;
@ApiModelProperty(value = "累计获取积分")
private int totalEarnedPoints;
@ApiModelProperty(value = "累计消费积分")
private int totalConsumedPoints;
@ApiModelProperty(value = "可用积分")
private int availablePoints;
@ApiModelProperty(value = "冻结积分")
private int frozenPoints;
public ZonePointsAccount toZonePointsAccount() {
ZonePointsAccount zonePointsAccount = new ZonePointsAccount();
zonePointsAccount.setId(id);
zonePointsAccount.setTotalPoints(totalPoints);
zonePointsAccount.setTotalEarnedPoints(totalEarnedPoints);
zonePointsAccount.setTotalConsumedPoints(totalConsumedPoints);
zonePointsAccount.setAvailablePoints(availablePoints);
zonePointsAccount.setFrozenPoints(frozenPoints);
return zonePointsAccount;
}
}

View File

@ -0,0 +1,18 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 积分申请详情VO含系统用户信息
*
* @author otto
*/
@Data
public class ZonePointsApplyDetailVo extends ZonePointsApply {
@ApiModelProperty("GitLink账号")
private String userName;
@ApiModelProperty("GitLink个人主页地址")
private String gitLinkUserUrl;
}

View File

@ -0,0 +1,81 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 积分申请输入对象
*
* @author otto
*/
@Data
@ApiModel("积分申请输入对象")
public class ZonePointsApplyInputVo {
@ApiModelProperty(value = "申请记录ID驳回操作时必填")
private Long id;
@ApiModelProperty(value = "用户ID", required = true)
@NotNull
private Long userId;
@ApiModelProperty(value = "专区ID", required = true)
@NotNull
private Long zoneId;
@ApiModelProperty(value = "组织ID", required = true)
@NotNull
private Long deptId;
@ApiModelProperty(value = "申请类型download下载/redeem兑换等", required = true)
@NotNull
private String applyType;
@ApiModelProperty(value = "事件ID关联积分流水事件")
private String eventId;
@ApiModelProperty(value = "申请积分数量")
private Integer points;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "真实姓名")
private String realName;
@ApiModelProperty(value = "描述")
private String description;
@ApiModelProperty(value = "回复/驳回内容")
private String reply;
@ApiModelProperty(value = "组织")
private String organization;
@ApiModelProperty(value = "微信号")
private String wechat;
@ApiModelProperty(value = "邮箱")
private String email;
public ZonePointsApply toZonePointsApply() {
ZonePointsApply zonePointsApply = new ZonePointsApply();
zonePointsApply.setUserId(userId);
zonePointsApply.setZoneId(zoneId);
zonePointsApply.setDeptId(deptId);
zonePointsApply.setApplyType(applyType);
zonePointsApply.setEventId(eventId);
zonePointsApply.setPoints(points);
zonePointsApply.setStatus("pending");
zonePointsApply.setPhone(phone);
zonePointsApply.setRealName(realName);
zonePointsApply.setDescription(description);
zonePointsApply.setOrganization(organization);
zonePointsApply.setWechat(wechat);
zonePointsApply.setEmail(email);
return zonePointsApply;
}
}

View File

@ -0,0 +1,46 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.common.core.web.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 积分申请搜索对象
*
* @author otto
*/
@Data
@ApiModel("积分申请搜索对象")
public class ZonePointsApplySearchVo extends BaseEntity {
@ApiModelProperty(value = "用户ID")
private Long userId;
@ApiModelProperty(value = "专区ID")
private Long zoneId;
@ApiModelProperty(value = "申请类型")
private String applyType;
@ApiModelProperty(value = "事件ID")
private String eventId;
@ApiModelProperty(value = "状态")
private String status;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "真实姓名")
private String realName;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "成员名称")
private String memberName;
@ApiModelProperty(value = "系统用户名")
private String userName;
}

View File

@ -0,0 +1,40 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 积分申请修改对象
*
* @author otto
*/
@Data
@ApiModel("积分申请修改对象")
public class ZonePointsApplyUpdateVo {
@ApiModelProperty(value = "主键ID", required = true)
private Long id;
@ApiModelProperty(value = "回复/响应内容")
private String reply;
@ApiModelProperty(value = "状态pending待处理/success成功/fail失败/rejected已拒绝")
private String status;
@ApiModelProperty(value = "事件ID关联积分流水事件")
private String eventId;
@ApiModelProperty(value = "回复人ID")
private Long replyId;
public ZonePointsApply toZonePointsApply() {
ZonePointsApply zonePointsApply = new ZonePointsApply();
zonePointsApply.setId(id);
zonePointsApply.setReply(reply);
zonePointsApply.setStatus(status);
zonePointsApply.setEventId(eventId);
zonePointsApply.setReplyId(replyId);
return zonePointsApply;
}
}

View File

@ -0,0 +1,27 @@
package com.microservices.zone.member.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 文章置顶积分扣减输入对象
*
* @author otto
*/
@Data
@ApiModel("文章置顶积分扣减请求")
public class ZonePointsArticleTopInputVo {
@ApiModelProperty(value = "组织ID", required = true)
private Long deptId;
@ApiModelProperty(value = "专区ID", required = true)
private Long zoneId;
@ApiModelProperty(value = "文章ID", required = true)
private Long id;
@ApiModelProperty(value = "文章名称", required = true)
private String name;
}

View File

@ -0,0 +1,32 @@
package com.microservices.zone.member.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 专区积分账户总览对象
*
* @author otto
*/
@Data
@ApiModel("专区积分账户总览对象")
public class ZonePointsOverviewVo {
@ApiModelProperty(value = "积分账户ID")
private Long accountId;
@ApiModelProperty(value = "专区ID")
private Long zoneId;
@ApiModelProperty(value = "累计获取积分")
private Integer totalEarnedPoints;
@ApiModelProperty(value = "累计消费积分")
private Integer totalConsumedPoints;
@ApiModelProperty(value = "当前可用积分")
private Integer availablePoints;
@ApiModelProperty(value = "冻结积分")
private Integer frozenPoints;
}

View File

@ -0,0 +1,27 @@
package com.microservices.zone.member.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 项目置顶积分扣减输入对象
*
* @author otto
*/
@Data
@ApiModel("项目置顶积分扣减请求")
public class ZonePointsProjectTopInputVo {
@ApiModelProperty(value = "组织ID", required = true)
private Long deptId;
@ApiModelProperty(value = "专区ID", required = true)
private Long zoneId;
@ApiModelProperty(value = "项目ID", required = true)
private Long id;
@ApiModelProperty(value = "项目名称", required = true)
private String name;
}

View File

@ -0,0 +1,55 @@
package com.microservices.zone.member.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 置顶文章信息返回对象
*
* @author otto
*/
@Data
@ApiModel("置顶文章信息")
public class ZonePointsTopDocVo {
@ApiModelProperty("专区积分文档记录ID")
private Long id;
@ApiModelProperty("专区ID")
private Long zoneId;
@ApiModelProperty("文章ID")
private Long docId;
@ApiModelProperty("文章名称")
private String docName;
@ApiModelProperty("文章概要")
private String summary;
@ApiModelProperty("是否积分置顶")
private Boolean isPointsTop;
@ApiModelProperty("置顶过期时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expiryTime;
@ApiModelProperty("文章头图")
private String headImg;
@ApiModelProperty("发布时间")
private String publishTime;
@ApiModelProperty("发布人")
private String publishUser;
@ApiModelProperty("浏览量")
private Long visits;
@ApiModelProperty("文章详情访问地址")
private String docDetailUrl;
}

View File

@ -0,0 +1,43 @@
package com.microservices.zone.member.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 置顶项目信息返回对象
*
* @author otto
*/
@Data
@ApiModel("置顶项目信息")
public class ZonePointsTopProjectVo {
@ApiModelProperty("项目聚合主键")
private Long id;
@ApiModelProperty("专区ID")
private Long zoneId;
@ApiModelProperty("项目名称")
private String name;
@ApiModelProperty("gitlink项目完整路径")
private String fullName;
@ApiModelProperty("gitlink项目ID")
private Long gitlinkProjectId;
@ApiModelProperty("是否积分置顶0代表不是 1代表是")
private String isPointsTop;
@ApiModelProperty("置顶过期时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expiryTime;
@ApiModelProperty("项目访问URL")
private String projectURL;
}

View File

@ -0,0 +1,87 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 积分流水输入对象合并事件+流水同时兼容原 ZonePointsEventInputVo API 入参
*
* @author otto
*/
@Data
@ApiModel("积分流水输入对象")
public class ZonePointsTransactionInputVo {
@ApiModelProperty(value = "事件ID")
private String eventId;
@ApiModelProperty(value = "专区IDAPI 入参,不存入流水表)")
private Long zoneId;
@ApiModelProperty(value = "用户IDAPI 入参,不存入流水表)")
private Long userId;
@ApiModelProperty(value = "事件类型")
private String eventType;
@ApiModelProperty(value = "积分账户ID", required = true)
@NotNull
private Long accountId;
@ApiModelProperty(value = "类型add增/reduce减/freezed冻结/unfreezed解冻", required = true)
@NotNull
private String transactionType;
@ApiModelProperty(value = "积分数量(正数)", required = true)
@NotNull
private Integer points;
@ApiModelProperty(value = "变动后可用积分")
private Integer availableAfter;
@ApiModelProperty(value = "变动后冻结积分")
private Integer frozenAfter;
@ApiModelProperty(value = "操作人ID")
private Long operatorId;
@ApiModelProperty(value = "变动原因")
private String reason;
@ApiModelProperty(value = "回复内容")
private String replyContent;
@ApiModelProperty(value = "来源ID")
private Long sourceId;
@ApiModelProperty(value = "来源数据")
private String sourceData;
@ApiModelProperty(value = "主题")
private String subject;
@ApiModelProperty(value = "主题描述")
private String subjectDesc;
public ZonePointsTransaction toZonePointsTransaction() {
ZonePointsTransaction zonePointsTransaction = new ZonePointsTransaction();
zonePointsTransaction.setEventId(eventId);
zonePointsTransaction.setEventType(eventType);
zonePointsTransaction.setAccountId(accountId);
zonePointsTransaction.setTransactionType(transactionType);
zonePointsTransaction.setPoints(points);
zonePointsTransaction.setAvailableAfter(availableAfter);
zonePointsTransaction.setFrozenAfter(frozenAfter);
zonePointsTransaction.setOperatorId(operatorId);
zonePointsTransaction.setReason(reason);
zonePointsTransaction.setReplyContent(replyContent);
zonePointsTransaction.setSourceId(sourceId);
zonePointsTransaction.setSourceData(sourceData);
zonePointsTransaction.setSubject(subject);
zonePointsTransaction.setSubjectDesc(subjectDesc);
return zonePointsTransaction;
}
}

View File

@ -0,0 +1,40 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 积分流水搜索对象合并事件+流水
*
* @author otto
*/
@Data
@ApiModel("积分流水搜索对象")
public class ZonePointsTransactionSearchVo {
@ApiModelProperty(value = "事件ID")
private String eventId;
@ApiModelProperty(value = "事件类型")
private String eventType;
@ApiModelProperty(value = "积分账户ID")
private Long accountId;
@ApiModelProperty(value = "类型add增/reduce减/freezed冻结/unfreezed解冻")
private String transactionType;
@ApiModelProperty(value = "来源ID")
private Long sourceId;
public ZonePointsTransaction toZonePointsTransaction() {
ZonePointsTransaction zonePointsTransaction = new ZonePointsTransaction();
zonePointsTransaction.setEventId(eventId);
zonePointsTransaction.setEventType(eventType);
zonePointsTransaction.setAccountId(accountId);
zonePointsTransaction.setTransactionType(transactionType);
zonePointsTransaction.setSourceId(sourceId);
return zonePointsTransaction;
}
}

View File

@ -0,0 +1,64 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 积分流水更新对象合并事件+流水
*
* @author otto
*/
@Data
@ApiModel("积分流水更新对象")
public class ZonePointsTransactionUpdateVo {
@ApiModelProperty(value = "流水ID", required = true)
@NotNull
private Long id;
@ApiModelProperty(value = "类型add增/reduce减/freezed冻结/unfreezed解冻")
private String transactionType;
@ApiModelProperty(value = "积分数量(正数)")
private int points;
@ApiModelProperty(value = "变动后可用积分")
private int availableAfter;
@ApiModelProperty(value = "变动后冻结积分")
private int frozenAfter;
@ApiModelProperty(value = "事件类型")
private String eventType;
@ApiModelProperty(value = "变动原因")
private String reason;
@ApiModelProperty(value = "回复内容")
private String replyContent;
@ApiModelProperty(value = "主题")
private String subject;
@ApiModelProperty(value = "主题描述")
private String subjectDesc;
public ZonePointsTransaction toZonePointsTransaction() {
ZonePointsTransaction zonePointsTransaction = new ZonePointsTransaction();
zonePointsTransaction.setId(id);
zonePointsTransaction.setTransactionType(transactionType);
zonePointsTransaction.setPoints(points);
zonePointsTransaction.setAvailableAfter(availableAfter);
zonePointsTransaction.setFrozenAfter(frozenAfter);
zonePointsTransaction.setEventType(eventType);
zonePointsTransaction.setReason(reason);
zonePointsTransaction.setReplyContent(replyContent);
zonePointsTransaction.setSubject(subject);
zonePointsTransaction.setSubjectDesc(subjectDesc);
return zonePointsTransaction;
}
}

View File

@ -0,0 +1,20 @@
package com.microservices.zone.member.domain.vo;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 专区积分流水带事件信息对象合并后不再需要单独事件关联保留此类用于前端兼容
*
* @author otto
* @deprecated 合并后可直接使用 ZonePointsTransaction此类仅用于 API 兼容过渡
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("积分流水带事件信息对象")
@Deprecated
public class ZonePointsTransactionWithEventVo extends ZonePointsTransaction {
// 合并后所有业务字段已在 ZonePointsTransaction 此类仅保留兼容
}

View File

@ -2,6 +2,7 @@ package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.ZoneMember;
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -72,6 +73,25 @@ public interface ZoneMemberMapper {
*/
ZoneMember selectZoneMemberByZoneIdAndUserId(@Param("zoneId") Long zoneId, @Param("userId") Long userId);
/**
* 根据用户Id和专区/组织查询专区会员审核状态不为审核未通过
*
* @param zoneId 专区ID与deptId二选一
* @param userId 用户ID
* @param deptId 组织ID与zoneId二选一
* @return
*/
ZoneMember selectZoneMember(@Param("zoneId") Long zoneId, @Param("userId") Long userId, @Param("deptId") Long deptId);
/**
* 查询专区会员信息含等级和积分账户
*
* @param zoneId 专区ID
* @param userId 用户ID
* @return 专区会员信息
*/
ZoneMember selectZoneMemberInfoByZoneIdAndUserId(@Param("zoneId") Long zoneId, @Param("userId") Long userId);
Integer selectZoneMemberListCount(ZoneMemberSearchVo zoneMemberSearchVo);
int updateZoneMemberHomePageShow(@Param("id") Long id, @Param("isHomepage") String isHomepage);
@ -102,6 +122,14 @@ public interface ZoneMemberMapper {
*/
Integer selectCountByZoneId(ZoneMember zoneMember);
/**
* 按会员分类统计专区下已审核会员数量
*
* @param zoneId 专区Id
* @return 每个分类id对应的已审核会员数量
*/
List<ZoneMemberTypeCountVo> selectAuditedMemberCountByType(@Param("zoneId") Long zoneId);
/**
* 获取指定序号的会员
*
@ -154,4 +182,45 @@ public interface ZoneMemberMapper {
* @return 会员组织Id列表
*/
List<Long> checkUserIsMemberInDepts(@Param("userId") Long userId, @Param("deptIds") Long[] deptIds);
/**
* 全局搜索 - 会员名称和用户名搜索含评分
*
* @param keyword 搜索关键词
* @param zoneId 专区ID
* @param offset 偏移量
* @param pageSize 每页数量
* @return 搜索结果列表含评分
*/
List<com.microservices.zone.member.domain.ZoneMemberWithScore> globalSearchWithScore(
@Param("keyword") String keyword,
@Param("zoneId") Long zoneId,
@Param("offset") int offset,
@Param("pageSize") int pageSize);
Long globalSearchCount(@Param("keyword") String keyword, @Param("zoneId") Long zoneId);
/**
* 全局搜索 - 扩展模糊搜索含会员分类和等级
*
* @param keyword 搜索关键词
* @param zoneId 专区ID
* @param offset 偏移量
* @param pageSize 每页数量
* @return 搜索结果列表含评分
*/
List<com.microservices.zone.member.domain.ZoneMemberWithScore> globalSearchWithScoreExtended(
@Param("keyword") String keyword,
@Param("zoneId") Long zoneId,
@Param("offset") int offset,
@Param("pageSize") int pageSize);
/**
* 全局搜索 - 扩展模糊搜索计数含会员分类和等级
*
* @param keyword 搜索关键词
* @param zoneId 专区ID
* @return 匹配总数
*/
Long globalSearchCountExtended(@Param("keyword") String keyword, @Param("zoneId") Long zoneId);
}

View File

@ -0,0 +1,126 @@
package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 专区积分账户Mapper接口
*
* @author otto
*/
@Mapper
public interface ZonePointsAccountMapper {
/**
* 查询专区积分账户
*
* @param id 专区积分账户主键
* @return 专区积分账户
*/
public ZonePointsAccount selectZonePointsAccountById(Long id);
/**
* 查询专区积分账户列表
*
* @param zonePointsAccount 专区积分账户
* @return 专区积分账户集合
*/
public List<ZonePointsAccount> selectZonePointsAccountList(ZonePointsAccount zonePointsAccount);
/**
* 新增专区积分账户
*
* @param zonePointsAccount 专区积分账户
* @return 结果
*/
public int insertZonePointsAccount(ZonePointsAccount zonePointsAccount);
/**
* 修改专区积分账户
*
* @param zonePointsAccount 专区积分账户
* @return 结果
*/
public int updateZonePointsAccount(ZonePointsAccount zonePointsAccount);
/**
* 删除专区积分账户
*
* @param id 专区积分账户主键
* @return 结果
*/
public int deleteZonePointsAccountById(Long id);
/**
* 批量删除专区积分账户
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteZonePointsAccountByIds(Long[] ids);
/**
* 根据用户ID和专区ID查询积分账户
*
* @param userId 用户ID
* @param zoneId 专区ID
* @return 专区积分账户
*/
ZonePointsAccount selectZonePointsAccountByUserIdAndZoneId(@Param("userId") Long userId, @Param("zoneId") Long zoneId);
/**
* 根据会员ID查询积分账户列表
*
* @param memberId 专区会员ID
* @return 专区积分账户列表
*/
List<ZonePointsAccount> selectZonePointsAccountByMemberId(@Param("memberId") Long memberId);
/**
* 锁定行获取账户信息用于并发安全更新
*
* @param id 账户ID
* @return 专区积分账户
*/
ZonePointsAccount selectZonePointsAccountByIdForUpdate(Long id);
/**
* 原子增加积分并发安全
*
* @param id 账户ID
* @param addPoints 增加的积分
* @return 影响行数
*/
int addPointsAtomic(@Param("id") Long id, @Param("addPoints") int addPoints);
/**
* 原子扣除积分并发安全
*
* @param id 账户ID
* @param deductPoints 扣除的积分
* @return 影响行数
*/
int deductPointsAtomic(@Param("id") Long id, @Param("deductPoints") int deductPoints);
int freezePointsAtomic(@Param("id") Long id, @Param("freezePoints") int freezePoints);
/**
* 原子解冻积分并发安全
*
* @param id 账户ID
* @param unfreezePoints 解冻的积分
* @return 影响行数
*/
int unfreezePointsAtomic(@Param("id") Long id, @Param("unfreezePoints") int unfreezePoints);
/**
* 原子扣减冻结积分并发安全用于申请通过时从冻结积分中正式扣除
*
* @param id 账户ID
* @param deductPoints 扣减的积分
* @return 影响行数
*/
int deductFrozenPointsAtomic(@Param("id") Long id, @Param("deductPoints") int deductPoints);
}

View File

@ -0,0 +1,83 @@
package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.points.ZonePointsApply;
import com.microservices.zone.member.domain.vo.ZonePointsApplySearchVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 积分申请表Mapper接口
*
* @author otto
*/
@Mapper
public interface ZonePointsApplyMapper {
/**
* 查询积分申请
*
* @param id 积分申请主键
* @return 积分申请
*/
public ZonePointsApply selectZonePointsApplyById(Long id);
/**
* 查询积分申请列表
*
* @param zonePointsApply 积分申请
* @return 积分申请集合
*/
public List<ZonePointsApply> selectZonePointsApplyList(ZonePointsApplySearchVo searchVo);
/**
* 新增积分申请
*
* @param zonePointsApply 积分申请
* @return 结果
*/
public int insertZonePointsApply(ZonePointsApply zonePointsApply);
/**
* 修改积分申请
*
* @param zonePointsApply 积分申请
* @return 结果
*/
public int updateZonePointsApply(ZonePointsApply zonePointsApply);
/**
* 删除积分申请
*
* @param id 积分申请主键
* @return 结果
*/
public int deleteZonePointsApplyById(Long id);
/**
* 批量删除积分申请
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteZonePointsApplyByIds(Long[] ids);
/**
* 根据用户ID和专区ID查询积分申请列表
*
* @param userId 用户ID
* @param zoneId 专区ID
* @return 积分申请列表
*/
List<ZonePointsApply> selectZonePointsApplyByUserIdAndZoneId(@Param("userId") Long userId, @Param("zoneId") Long zoneId);
/**
* 统计用户某类型待处理的申请数量幂等校验
*
* @param userId 用户ID
* @param zoneId 专区ID
* @param applyType 申请类型
* @return 待处理申请数量
*/
int countPendingByUserZoneAndType(@Param("userId") Long userId, @Param("zoneId") Long zoneId, @Param("applyType") String applyType);
}

View File

@ -0,0 +1,92 @@
package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.points.ZonePointsDoc;
import com.microservices.zone.member.domain.vo.ZonePointsTopDocVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 专区积分文档Mapper接口
*
* @author otto
*/
@Mapper
public interface ZonePointsDocMapper {
/**
* 查询专区积分文档
*
* @param id 主键ID
* @return 专区积分文档
*/
ZonePointsDoc selectZonePointsDocById(Long id);
/**
* 查询专区积分文档
*
* @param zoneId 专区ID
* @param docId 文章ID
* @return 专区积分文档
*/
ZonePointsDoc selectZonePointsDocByZoneIdAndDocId(@Param("zoneId") Long zoneId, @Param("docId") Long docId);
/**
* 新增专区积分文档
*
* @param zonePointsDoc 专区积分文档
* @return 结果
*/
int insertZonePointsDoc(ZonePointsDoc zonePointsDoc);
/**
* 修改专区积分文档
*
* @param zonePointsDoc 专区积分文档
* @return 结果
*/
int updateZonePointsDoc(ZonePointsDoc zonePointsDoc);
/**
* 增加触发次数原子操作
*
* @param id 主键ID
* @param addCount 增加的触发次数
* @return 影响行数
*/
int incrementTriggerCount(@Param("id") Long id, @Param("addCount") int addCount);
/**
* 检查用户在指定专区下是否已有置顶文章
*
* @param zoneId 专区ID
* @param userId 用户ID
* @return 置顶文章记录数
*/
int hasPointsTopByZoneIdAndUserId(@Param("zoneId") Long zoneId, @Param("userId") Long userId);
/**
* 查询用户在指定专区下的置顶文章
*
* @param zoneId 专区ID
* @param userId 用户ID
* @return 置顶文章记录
*/
ZonePointsDoc selectPointsTopByZoneIdAndUserId(@Param("zoneId") Long zoneId, @Param("userId") Long userId);
/**
* 查询专区下置顶文章列表左联 cms_doc过期时间晚于当前时间用于前台展示
*
* @param zoneId 专区ID
* @return 置顶文章列表含文章名称和概要
*/
List<ZonePointsTopDocVo> selectTopDocList(@Param("zoneId") Long zoneId);
/**
* 批量取消已过期的积分置顶is_points_top=1 expiry_time 早于当前时间
*
* @return 影响行数
*/
int updateExpiredPointsTop();
}

View File

@ -0,0 +1,21 @@
package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.points.ZonePointsFailLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 积分异步任务失败记录Mapper接口
*
* @author otto
*/
@Mapper
public interface ZonePointsFailLogMapper {
/**
* 新增积分异步任务失败记录
*
* @param zonePointsFailLog 失败记录
* @return 结果
*/
int insertZonePointsFailLog(ZonePointsFailLog zonePointsFailLog);
}

View File

@ -0,0 +1,137 @@
package com.microservices.zone.member.mapper;
import com.microservices.zone.member.domain.points.ZonePointsTransaction;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* 专区积分流水Mapper接口合并事件+流水
*
* @author otto
*/
@Mapper
public interface ZonePointsTransactionMapper {
/**
* 查询专区积分流水
*
* @param id 专区积分流水主键
* @return 专区积分流水
*/
public ZonePointsTransaction selectZonePointsTransactionById(Long id);
/**
* 查询专区积分流水列表
*
* @param zonePointsTransaction 专区积分流水
* @return 专区积分流水集合
*/
public List<ZonePointsTransaction> selectZonePointsTransactionList(ZonePointsTransaction zonePointsTransaction);
/**
* 新增专区积分流水
*
* @param zonePointsTransaction 专区积分流水
* @return 结果
*/
public int insertZonePointsTransaction(ZonePointsTransaction zonePointsTransaction);
/**
* 修改专区积分流水
*
* @param zonePointsTransaction 专区积分流水
* @return 结果
*/
public int updateZonePointsTransaction(ZonePointsTransaction zonePointsTransaction);
/**
* 删除专区积分流水
*
* @param id 专区积分流水主键
* @return 结果
*/
public int deleteZonePointsTransactionById(Long id);
/**
* 批量删除专区积分流水
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteZonePointsTransactionByIds(Long[] ids);
/**
* 根据事件ID查询流水列表
*
* @param eventId 事件ID
* @return 专区积分流水列表
*/
List<ZonePointsTransaction> selectZonePointsTransactionByEventId(@Param("eventId") String eventId);
/**
* 根据账户ID查询流水列表
*
* @param accountId 积分账户ID
* @return 专区积分流水列表
*/
List<ZonePointsTransaction> selectZonePointsTransactionByAccountId(@Param("accountId") Long accountId);
/**
* 根据用户ID和专区ID查询流水列表
*
* @param userId 用户ID
* @param zoneId 专区ID
* @return 专区积分流水列表
*/
List<ZonePointsTransaction> selectZonePointsTransactionByUserIdAndZoneId(@Param("userId") Long userId, @Param("zoneId") Long zoneId);
/**
* 根据专区ID获取当前用户积分明细分页
*
* @param zoneId 专区ID
* @param userId 用户ID
* @param transactionType 交易类型可选
* @return 专区积分流水列表
*/
List<ZonePointsTransaction> selectMyPointsTransactionDetailsByZone(@Param("zoneId") Long zoneId, @Param("userId") Long userId, @Param("transactionType") String transactionType);
/**
* 查询当前用户流水列表合并后直接从流水表查询包含所有业务字段
*
* @param userId 用户ID
* @param zoneId 专区ID
* @return 专区积分流水列表
*/
List<ZonePointsTransaction> selectCurrentUserTransactionWithEvent(@Param("userId") Long userId, @Param("zoneId") Long zoneId);
/**
* 统计指定事件类型来源ID账户ID的流水记录数用于幂等校验
*/
int countByEventTypeAndSourceAndUser(@Param("eventType") String eventType,
@Param("sourceId") Long sourceId,
@Param("accountId") Long accountId);
/**
* 统计指定事件类型专区ID用户ID的流水记录数用于幂等校验JOIN account 获取 zoneId
*/
int countByEventTypeAndZoneAndUser(@Param("eventType") String eventType,
@Param("zoneId") Long zoneId,
@Param("userId") Long userId);
/**
* 根据专区ID用户ID和事件类型查询积分流水限制一条JOIN account 获取 zoneId/userId
*/
ZonePointsTransaction selectPointsTransactionOne(@Param("zoneId") Long zoneId,
@Param("userId") Long userId,
@Param("eventType") String eventType);
/**
* 统计指定账户事件类型在给定时间范围内的流水记录数用于幂等校验日期约束在 SQL 层避免脏数据干扰
*/
int countByAccountIdAndEventTypeAndDateRange(@Param("accountId") Long accountId,
@Param("eventType") String eventType,
@Param("startTime") Date startTime,
@Param("endTime") Date endTime);
}

View File

@ -31,6 +31,14 @@ public interface IZoneMemberService {
*/
public GenericsTableDataInfo<ZoneMemberDetailVo> selectZoneMemberList(ZoneMemberSearchVo zoneMemberSearchVo, boolean isOpen);
/**
* 查询特色专区已审核会员列表公开接口不分页由调用方通过 PageHelper 控制
*
* @param zoneMemberSearchVo 特色专区会员查询对象zoneIdnamememberTypeIdmemberLevelId
* @return 特色专区会员集合
*/
public GenericsTableDataInfo<ZoneMemberDataVo> selectOpenZoneMemberList(ZoneMemberSearchVo zoneMemberSearchVo);
/**
* 新增特色专区会员
*
@ -97,6 +105,15 @@ public interface IZoneMemberService {
*/
int updateZoneMemberHomePageShow(Long id);
/**
* 根据专区ID修改当前登录用户会员信息
*
* @param zoneId 专区ID
* @param zoneMemberUpdateVo 会员更新信息
* @return 结果
*/
int updateZoneMemberByMember(Long zoneId, ZoneMemberUpdateVo zoneMemberUpdateVo);
/**
* 获取特色专区项目会员总览列表
*
@ -136,6 +153,8 @@ public interface IZoneMemberService {
*/
String selectZoneMemberAuditStatus(Long zoneId);
ZoneMember selectZoneMemberInfo(Long zoneId);
Integer selectZoneMemberAuditCount(ZoneMemberSearchVo zoneMemberSearchVo);
/**

View File

@ -1,6 +1,7 @@
package com.microservices.zone.member.service;
import com.microservices.zone.member.domain.ZoneMemberType;
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
import com.microservices.zone.member.domain.vo.ZoneMemberTypeUpdateVo;
import java.util.List;
@ -28,6 +29,15 @@ public interface IZoneMemberTypeService {
*/
public List<ZoneMemberType> selectZoneMemberTypeList(ZoneMemberType zoneMemberType);
/**
* 查询特色专区会员分类列表含各分类下已审核会员数量
*
* @param zoneId 专区Id
* @param name 会员分类名称模糊搜索可为空
* @return 会员分类集合含已审核会员数量
*/
public List<ZoneMemberTypeCountVo> selectZoneMemberTypeListWithCount(Long zoneId, String name);
/**
* 新增特色专区会员分类
*

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