推送项目重构代码
This commit is contained in:
@@ -24,6 +24,10 @@ import com.ruoyi.oa.domain.vo.OaProjectScheduleVo;
|
||||
import com.ruoyi.oa.domain.bo.OaProjectScheduleBo;
|
||||
import com.ruoyi.oa.service.IOaProjectScheduleService;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.oa.im.ImSendService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* 项目进度
|
||||
@@ -38,6 +42,43 @@ import com.ruoyi.common.core.page.TableDataInfo;
|
||||
public class OaProjectScheduleController extends BaseController {
|
||||
|
||||
private final IOaProjectScheduleService iOaProjectScheduleService;
|
||||
private final ImSendService imSendService;
|
||||
private final ISysUserService userService;
|
||||
|
||||
/**
|
||||
* 催促进度:给项目负责人发一条 IM 消息
|
||||
*/
|
||||
@PostMapping("/urge/{scheduleId}")
|
||||
public R<Void> urgeProgress(@PathVariable Long scheduleId) {
|
||||
OaProjectScheduleVo vo = iOaProjectScheduleService.queryById(scheduleId);
|
||||
if (vo == null) {
|
||||
return R.fail("项目进度不存在");
|
||||
}
|
||||
String header = vo.getHeader();
|
||||
if (header == null || header.isEmpty()) {
|
||||
return R.fail("该项目未设置负责人");
|
||||
}
|
||||
SysUser user = userService.selectUserByNickName(header);
|
||||
if (user == null) {
|
||||
return R.fail("找不到负责人 " + header + " 的账号");
|
||||
}
|
||||
// 防止自己催自己 → 还是发,但提示
|
||||
String me = LoginHelper.getNickName();
|
||||
String projectName = vo.getProjectName() == null ? "(未命名项目)" : vo.getProjectName();
|
||||
String currentStep = vo.getCurrentStepName() == null ? "(无)" : vo.getCurrentStepName();
|
||||
int percent = vo.getSchedulePercentage() == null ? 0 : vo.getSchedulePercentage().intValue();
|
||||
long delay = vo.getDelayCount() == null ? 0 : vo.getDelayCount();
|
||||
|
||||
String title = "进度催促";
|
||||
String desc = String.format("[%s] 催促您推进【%s】,当前 %d%%,当前步骤:%s%s",
|
||||
me == null ? "系统" : me, projectName, percent, currentStep,
|
||||
delay > 0 ? ",已有 " + delay + " 步延期" : "");
|
||||
|
||||
imSendService.sendToOaUser(user.getUserId(), title, desc,
|
||||
"schedule", scheduleId, "/projectSchedule?scheduleId=" + scheduleId);
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目进度列表
|
||||
|
||||
@@ -57,6 +57,9 @@ public class SysOaWarehouseMaster extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/** 收货单/相关附件 OSS ID(逗号分隔) */
|
||||
private String receiptDoc;
|
||||
|
||||
private Integer isLike;
|
||||
private Long status;
|
||||
private Integer returnType;
|
||||
|
||||
@@ -61,6 +61,9 @@ public class SysOaWarehouseMasterBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/** 收货单/相关附件 OSS ID(逗号分隔) */
|
||||
private String receiptDoc;
|
||||
|
||||
/**
|
||||
* 涉及物料
|
||||
*/
|
||||
|
||||
@@ -97,5 +97,9 @@ public class SysOaWarehouseMasterVo {
|
||||
private Long totalQty;
|
||||
/** 物料概览(GROUP_CONCAT 名称) */
|
||||
private String itemsSummary;
|
||||
/** 收货单 OSS ID(CSV) */
|
||||
private String receiptDoc;
|
||||
/** 收货单文件信息(已联查 sys_oss,格式 ossId|name|url,, ) */
|
||||
private String receiptFiles;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ruoyi.oa.im;
|
||||
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.oa.im.mapper.ImBindMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 当前用户的 IM 凭据(供 Web SDK 登录)
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/system/user/im")
|
||||
public class ImCredentialsController extends BaseController {
|
||||
|
||||
private final ImBindMapper bindMapper;
|
||||
private final OpenImClient openImClient;
|
||||
private final OpenImProperties props;
|
||||
|
||||
/** 当前用户 IM 登录所需信息:imUserId + token + apiUrl + wsUrl */
|
||||
@GetMapping("/credentials")
|
||||
public R<Map<String, Object>> credentials() {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
ImBind bind = bindMapper.selectById(userId);
|
||||
if (bind == null || bind.getImUserId() == null) {
|
||||
return R.fail("当前账号未绑定 IM");
|
||||
}
|
||||
// platformId=5: Web
|
||||
String token = openImClient.issueUserToken(bind.getImUserId(), 5);
|
||||
if (token == null) {
|
||||
return R.fail("获取 IM token 失败");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("imUserId", bind.getImUserId());
|
||||
data.put("imToken", token);
|
||||
data.put("apiUrl", props.getPublicApiUrl());
|
||||
data.put("wsUrl", props.getWsUrl());
|
||||
return R.ok(data);
|
||||
}
|
||||
}
|
||||
@@ -106,20 +106,21 @@ public class OpenImClient {
|
||||
offlinePush.put("iOSPushSound", "default");
|
||||
offlinePush.put("iOSBadgeCount", true);
|
||||
|
||||
Map<String, Object> sendMsg = new HashMap<>();
|
||||
sendMsg.put("sendID", props.getNotificationSender());
|
||||
sendMsg.put("recvID", recvImUserId);
|
||||
sendMsg.put("senderNickname", "系统通知");
|
||||
sendMsg.put("senderPlatformID", 1);
|
||||
sendMsg.put("content", content);
|
||||
sendMsg.put("contentType", CUSTOM_CONTENT_TYPE);
|
||||
sendMsg.put("sessionType", SESSION_SINGLE);
|
||||
sendMsg.put("isOnlineOnly", false);
|
||||
sendMsg.put("notOfflinePush", false);
|
||||
sendMsg.put("offlinePushInfo", offlinePush);
|
||||
// SendMsg 字段需要嵌套在 sendMessage 对象里(OpenIM v3.8 约定)
|
||||
Map<String, Object> sendMessage = new HashMap<>();
|
||||
sendMessage.put("sendID", props.getNotificationSender());
|
||||
sendMessage.put("recvID", recvImUserId);
|
||||
sendMessage.put("senderNickname", "系统通知");
|
||||
sendMessage.put("senderPlatformID", 1);
|
||||
sendMessage.put("content", content);
|
||||
sendMessage.put("contentType", CUSTOM_CONTENT_TYPE);
|
||||
sendMessage.put("sessionType", SESSION_SINGLE);
|
||||
sendMessage.put("isOnlineOnly", false);
|
||||
sendMessage.put("notOfflinePush", false);
|
||||
sendMessage.put("offlinePushInfo", offlinePush);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("sendMsg", sendMsg);
|
||||
body.put("sendMessage", sendMessage);
|
||||
|
||||
JSONObject resp = postJson(props.getApiUrl() + "/msg/send_msg", body, getAdminToken());
|
||||
Integer errCode = resp.getInteger("errCode");
|
||||
@@ -130,6 +131,26 @@ public class OpenImClient {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 admin 身份签发 IM 用户 token(Web SDK 登录需要)。
|
||||
* @param imUserId IM userID
|
||||
* @param platformId 平台编号(5=Web)
|
||||
*/
|
||||
public String issueUserToken(String imUserId, int platformId) {
|
||||
if (!props.isEnabled()) { return null; }
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("secret", props.getSecret());
|
||||
body.put("platformID", platformId);
|
||||
body.put("userID", imUserId);
|
||||
JSONObject resp = postJson(props.getApiUrl() + "/auth/get_user_token", body, getAdminToken());
|
||||
JSONObject data = resp.getJSONObject("data");
|
||||
if (data == null) {
|
||||
log.warn("[OpenIM] issueUserToken failed: {}", resp);
|
||||
return null;
|
||||
}
|
||||
return data.getString("token");
|
||||
}
|
||||
|
||||
/** chat 后端 admin token */
|
||||
public String getChatAdminToken() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
@@ -14,14 +14,20 @@ import org.springframework.stereotype.Component;
|
||||
@ConfigurationProperties(prefix = "openim")
|
||||
public class OpenImProperties {
|
||||
|
||||
/** OpenIM 核心 API 地址 */
|
||||
private String apiUrl = "http://49.232.154.205:10002";
|
||||
/** OpenIM 核心 API 地址(走 nginx 网关,:10002 直连端口外网未开放) */
|
||||
private String apiUrl = "http://49.232.154.205:10006/api";
|
||||
|
||||
/** OpenIM chat 业务 API 地址 */
|
||||
private String chatUrl = "http://49.232.154.205:10008";
|
||||
/** OpenIM WS 长连接地址(前端 SDK 用) */
|
||||
private String wsUrl = "ws://49.232.154.205:10006/msg_gateway";
|
||||
|
||||
/** OpenIM chat 管理 API 地址(注册新用户、admin login) */
|
||||
private String chatAdminUrl = "http://49.232.154.205:10009";
|
||||
/** 前端 SDK 可以直接访问的 API 地址(如果跟后端调用地址不同) */
|
||||
private String publicApiUrl = "http://49.232.154.205:10006/api";
|
||||
|
||||
/** OpenIM chat 业务 API 地址(走 nginx 网关) */
|
||||
private String chatUrl = "http://49.232.154.205:10006/chat";
|
||||
|
||||
/** OpenIM chat 管理 API 地址(走 nginx 网关) */
|
||||
private String chatAdminUrl = "http://49.232.154.205:10006/chat";
|
||||
|
||||
/** chat 后端 admin 账号 */
|
||||
private String chatAdminAccount = "chatAdmin";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ruoyi.oa.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -18,5 +19,7 @@ import java.util.Map;
|
||||
*/
|
||||
public interface OaProjectScheduleMapper extends BaseMapperPlus<OaProjectScheduleMapper, OaProjectSchedule, OaProjectScheduleVo> {
|
||||
|
||||
/** dataPermission="true" 让 PlusDataPermissionInterceptor 跳过解析(SQL 太复杂 JsqlParser 不识别) */
|
||||
@InterceptorIgnore(dataPermission = "true")
|
||||
Page<OaProjectScheduleVo> selectVoPagePlus(@Param("page") Page<OaProjectScheduleVo> build,@Param(Constants.WRAPPER) QueryWrapper<OaProjectSchedule> lqw);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.ruoyi.oa.suggestion;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户修改意见(区别于已有的 oa_feedback 问题反馈,本表为顶栏入口提交的改进意见)
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_oa_feedback")
|
||||
public class UserSuggestion {
|
||||
|
||||
@TableId(value = "feedback_id")
|
||||
private Long feedbackId;
|
||||
|
||||
private String title;
|
||||
private String content;
|
||||
private String category;
|
||||
private Integer priority;
|
||||
private String pagePath;
|
||||
private String attachment;
|
||||
|
||||
private Long submitterId;
|
||||
private String submitterName;
|
||||
|
||||
/** 0待处理 1已受理 2已完成 3已关闭 */
|
||||
private Integer status;
|
||||
|
||||
private Long handlerId;
|
||||
private String handlerName;
|
||||
private String acceptRemark;
|
||||
private Date acceptTime;
|
||||
private Date finishTime;
|
||||
|
||||
private String createBy;
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
private Date updateTime;
|
||||
private Integer delFlag;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.ruoyi.oa.suggestion;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.oa.im.ImSendService;
|
||||
import com.ruoyi.oa.suggestion.mapper.UserSuggestionMapper;
|
||||
import com.ruoyi.system.mapper.SysUserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户修改意见
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/oa/suggestion")
|
||||
public class UserSuggestionController extends BaseController {
|
||||
|
||||
/** 信息化部 dept_id */
|
||||
private static final Long IT_DEPT_ID = 1858416909042524161L;
|
||||
|
||||
private final UserSuggestionMapper mapper;
|
||||
private final ImSendService imSendService;
|
||||
private final SysUserMapper userMapper;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<UserSuggestion> list(UserSuggestion query, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<UserSuggestion> qw = Wrappers.lambdaQuery();
|
||||
qw.eq(UserSuggestion::getDelFlag, 0);
|
||||
Long uid = LoginHelper.getUserId();
|
||||
if (!isItDeptMember(uid)) {
|
||||
qw.eq(UserSuggestion::getSubmitterId, uid);
|
||||
}
|
||||
if (query.getStatus() != null) qw.eq(UserSuggestion::getStatus, query.getStatus());
|
||||
if (query.getCategory() != null && !query.getCategory().isEmpty()) {
|
||||
qw.eq(UserSuggestion::getCategory, query.getCategory());
|
||||
}
|
||||
if (query.getTitle() != null && !query.getTitle().isEmpty()) {
|
||||
qw.like(UserSuggestion::getTitle, query.getTitle());
|
||||
}
|
||||
qw.orderByDesc(UserSuggestion::getCreateTime);
|
||||
Page<UserSuggestion> result = mapper.selectPage(pageQuery.build(), qw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public R<Void> submit(@RequestBody UserSuggestion body) {
|
||||
Long uid = LoginHelper.getUserId();
|
||||
String name = LoginHelper.getNickName();
|
||||
body.setFeedbackId(null);
|
||||
body.setSubmitterId(uid);
|
||||
body.setSubmitterName(name);
|
||||
body.setStatus(0);
|
||||
body.setCreateBy(name);
|
||||
body.setCreateTime(new Date());
|
||||
body.setDelFlag(0);
|
||||
mapper.insert(body);
|
||||
|
||||
List<SysUser> itUsers = userMapper.selectList(
|
||||
Wrappers.<SysUser>lambdaQuery()
|
||||
.eq(SysUser::getDeptId, IT_DEPT_ID)
|
||||
.eq(SysUser::getDelFlag, "0"));
|
||||
String title = "新的修改意见";
|
||||
String desc = String.format("[%s] %s", name == null ? "用户" : name,
|
||||
body.getTitle() == null ? "未命名" : body.getTitle());
|
||||
for (SysUser u : itUsers) {
|
||||
if (u.getUserId() == null || u.getUserId().equals(uid)) continue;
|
||||
imSendService.sendToOaUser(u.getUserId(), title, desc,
|
||||
"suggestion", body.getFeedbackId(),
|
||||
"/system/feedback?id=" + body.getFeedbackId());
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/accept")
|
||||
public R<Void> accept(@PathVariable Long id, @RequestParam(required = false) String remark) {
|
||||
UserSuggestion fb = mapper.selectById(id);
|
||||
if (fb == null) return R.fail("不存在");
|
||||
if (!isItDeptMember(LoginHelper.getUserId())) return R.fail("仅信息化部可操作");
|
||||
fb.setStatus(1);
|
||||
fb.setHandlerId(LoginHelper.getUserId());
|
||||
fb.setHandlerName(LoginHelper.getNickName());
|
||||
fb.setAcceptRemark(remark);
|
||||
fb.setAcceptTime(new Date());
|
||||
mapper.updateById(fb);
|
||||
if (fb.getSubmitterId() != null) {
|
||||
imSendService.sendToOaUser(fb.getSubmitterId(),
|
||||
"您的意见已受理",
|
||||
String.format("[%s] 受理了您的意见:%s%s",
|
||||
fb.getHandlerName() == null ? "信息化部" : fb.getHandlerName(),
|
||||
fb.getTitle(),
|
||||
remark == null || remark.isEmpty() ? "" : "(备注:" + remark + ")"),
|
||||
"suggestion", id, "/system/feedback?id=" + id);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/finish")
|
||||
public R<Void> finish(@PathVariable Long id, @RequestParam(required = false) String remark) {
|
||||
UserSuggestion fb = mapper.selectById(id);
|
||||
if (fb == null) return R.fail("不存在");
|
||||
if (!isItDeptMember(LoginHelper.getUserId())) return R.fail("仅信息化部可操作");
|
||||
fb.setStatus(2);
|
||||
fb.setFinishTime(new Date());
|
||||
if (remark != null) fb.setAcceptRemark(remark);
|
||||
mapper.updateById(fb);
|
||||
if (fb.getSubmitterId() != null) {
|
||||
imSendService.sendToOaUser(fb.getSubmitterId(),
|
||||
"您的意见已完成",
|
||||
String.format("[%s] 已处理完毕:%s", fb.getHandlerName(), fb.getTitle()),
|
||||
"suggestion", id, "/system/feedback?id=" + id);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/close")
|
||||
public R<Void> close(@PathVariable Long id, @RequestParam(required = false) String remark) {
|
||||
UserSuggestion fb = mapper.selectById(id);
|
||||
if (fb == null) return R.fail("不存在");
|
||||
Long uid = LoginHelper.getUserId();
|
||||
if (!isItDeptMember(uid) && !uid.equals(fb.getSubmitterId())) return R.fail("无权关闭");
|
||||
fb.setStatus(3);
|
||||
if (remark != null) fb.setAcceptRemark(remark);
|
||||
mapper.updateById(fb);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/isItMember")
|
||||
public R<Boolean> isItMember() {
|
||||
return R.ok(isItDeptMember(LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
private boolean isItDeptMember(Long userId) {
|
||||
if (userId == null) return false;
|
||||
SysUser u = userMapper.selectById(userId);
|
||||
return u != null && IT_DEPT_ID.equals(u.getDeptId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ruoyi.oa.suggestion.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.oa.suggestion.UserSuggestion;
|
||||
|
||||
public interface UserSuggestionMapper extends BaseMapperPlus<UserSuggestionMapper, UserSuggestion, UserSuggestion> {
|
||||
}
|
||||
Reference in New Issue
Block a user