diff --git a/.deploy/nginx-rebuild.conf b/.deploy/nginx-rebuild.conf index 8e8f4d6a7..f49229b21 100644 --- a/.deploy/nginx-rebuild.conf +++ b/.deploy/nginx-rebuild.conf @@ -1,25 +1,23 @@ +# include nginx-rebuild.conf server { - server_name YOUR_DOMAIN; - listen 80; - # HTTPS - #listen 443 ssl http2; - #ssl_certificate ssl.crt; - #ssl_certificate_key ssl.key; - # PROXY - proxy_redirect http:// $scheme://; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - location / { - proxy_pass http://localhost:18080; - etag on; - } - location /assets { - proxy_pass http://localhost:18080/assets; - expires 90d; - } - #location /h5-app { - # alias /data/rebuild/h5-app; - # etag on; - #} -} \ No newline at end of file + server_name YOUR_DOMAIN; + listen 80; + # HTTPS + #listen 443 ssl http2; + #ssl_certificate /path/to/ssl.crt; + #ssl_certificate_key /path/to/ssl.key; + # PROXY + proxy_redirect http:// $scheme://; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + location / { + proxy_pass http://127.0.0.1:18080; + etag on; + } + location /h5app { + alias /path/to/public/h5app; + try_files $uri $uri/ /h5app/index.html; + etag on; + } +} diff --git a/.gitignore b/.gitignore index 47412033f..41c374639 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,7 @@ node .DS_Store test.* + +# Build + +**/public/h5app/ diff --git a/@rbv b/@rbv index fa267c9c6..28379ae7d 160000 --- a/@rbv +++ b/@rbv @@ -1 +1 @@ -Subproject commit fa267c9c6721819f19208a3afd0f6dff6b14f620 +Subproject commit 28379ae7d7f3d76b203f889f877ec7b6deb690b7 diff --git a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java index 988c6afff..c857da444 100644 --- a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java +++ b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java @@ -452,7 +452,7 @@ public class FormsBuilder extends FormsManager { .append(" where ") .append(entity.getPrimaryField().getName()) .append(" = ?"); - return Application.getQueryFactory().createQuery(sql.toString(), user).setParameter(1, id).record(); + return Application.createQuery(sql.toString(), user).setParameter(1, id).record(); } /** diff --git a/src/main/java/com/rebuild/core/service/approval/ApprovalStepService.java b/src/main/java/com/rebuild/core/service/approval/ApprovalStepService.java index f37ea006d..24a6405ae 100644 --- a/src/main/java/com/rebuild/core/service/approval/ApprovalStepService.java +++ b/src/main/java/com/rebuild/core/service/approval/ApprovalStepService.java @@ -15,6 +15,8 @@ import com.rebuild.core.UserContextHolder; import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.metadata.MetadataHelper; import com.rebuild.core.metadata.easymeta.EasyMetaFactory; +import com.rebuild.core.privileges.OperationDeniedException; +import com.rebuild.core.privileges.UserHelper; import com.rebuild.core.privileges.UserService; import com.rebuild.core.service.BaseService; import com.rebuild.core.service.DataSpecificationNoRollbackException; @@ -252,6 +254,10 @@ public class ApprovalStepService extends BaseService { final ID opUser = UserContextHolder.getUser(); final ApprovalState useState = isRevoke ? ApprovalState.REVOKED : ApprovalState.CANCELED; + if (isRevoke && !UserHelper.isAdmin(opUser)) { + throw new OperationDeniedException(Language.L("仅管理员可撤销审批")); + } + Record step = EntityHelper.forNew(EntityHelper.RobotApprovalStep, opUser); step.setID("recordId", recordId); step.setID("approvalId", approvalId == null ? APPROVAL_NOID : approvalId); diff --git a/src/main/java/com/rebuild/core/service/general/BulkOperator.java b/src/main/java/com/rebuild/core/service/general/BulkOperator.java index fb2108a6b..b4e1222dc 100644 --- a/src/main/java/com/rebuild/core/service/general/BulkOperator.java +++ b/src/main/java/com/rebuild/core/service/general/BulkOperator.java @@ -73,7 +73,7 @@ public abstract class BulkOperator extends HeavyTask { entity.getPrimaryField().getName(), entity.getName(), sqlWhere); // NOTE 注意没有分页 - Query query = Application.getQueryFactory().createQuery(sql, context.getOpUser()); + Query query = Application.createQuery(sql, context.getOpUser()); Object[][] array = QueryHelper.readArray(query); Set ids = new HashSet<>(); for (Object[] o : array) { diff --git a/src/main/java/com/rebuild/core/service/project/ProjectConfigService.java b/src/main/java/com/rebuild/core/service/project/ProjectConfigService.java index 869328163..d18b4f032 100644 --- a/src/main/java/com/rebuild/core/service/project/ProjectConfigService.java +++ b/src/main/java/com/rebuild/core/service/project/ProjectConfigService.java @@ -53,7 +53,7 @@ public class ProjectConfigService extends BaseConfigurationService implements Ad @Override public int delete(ID projectId) { - Object[] count = Application.createQuery( + Object[] count = Application.createQueryNoFilter( "select count(taskId) from ProjectTask where projectId = ?") .setParameter(1, projectId) .unique(); @@ -77,9 +77,12 @@ public class ProjectConfigService extends BaseConfigurationService implements Ad // 使用模板 if (useTemplate == TEMPLATE_DEFAULT) { - ID id1 = createPlan(project.getPrimary(), Language.L("待处理"), 1000, ProjectPlanConfigService.FLOW_STATUS_START, null); - ID id2 = createPlan(project.getPrimary(), Language.L("进行中"), 2000, ProjectPlanConfigService.FLOW_STATUS_PROCESSING, null); - ID id3 = createPlan(project.getPrimary(), Language.L("已完成"), 3000, ProjectPlanConfigService.FLOW_STATUS_END, new ID[]{id1, id2}); + ID id1 = createPlan(project.getPrimary(), + Language.L("待处理"), 1000, ProjectPlanConfigService.FLOW_STATUS_START, null); + ID id2 = createPlan(project.getPrimary(), + Language.L("进行中"), 2000, ProjectPlanConfigService.FLOW_STATUS_PROCESSING, null); + ID id3 = createPlan(project.getPrimary(), + Language.L("已完成"), 3000, ProjectPlanConfigService.FLOW_STATUS_END, new ID[]{id1, id2}); updateFlowNexts(id1, new ID[]{id2, id3}); updateFlowNexts(id2, new ID[]{id1, id3}); } diff --git a/src/main/java/com/rebuild/core/service/project/ProjectTaskService.java b/src/main/java/com/rebuild/core/service/project/ProjectTaskService.java index 2505287ee..ca0b836e9 100644 --- a/src/main/java/com/rebuild/core/service/project/ProjectTaskService.java +++ b/src/main/java/com/rebuild/core/service/project/ProjectTaskService.java @@ -16,6 +16,7 @@ import com.rebuild.core.UserContextHolder; import com.rebuild.core.configuration.ConfigBean; import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.privileges.OperationDeniedException; +import com.rebuild.core.service.DataSpecificationException; import com.rebuild.core.service.notification.Message; import com.rebuild.core.service.notification.MessageBuilder; import com.rebuild.core.support.i18n.Language; @@ -50,9 +51,16 @@ public class ProjectTaskService extends BaseTaskService { final ID user = UserContextHolder.getUser(); checkInMembers(user, record.getID("projectId")); - record.setLong("taskNumber", getNextTaskNumber(record.getID("projectId"))); - applyFlowStatue(record); - record.setInt("seq", getNextSeqViaMidValue(record.getID("projectPlanId"))); + ID projectId = record.getID("projectId"); + ID projectPlanId = record.getID("projectPlanId"); + ConfigBean p = ProjectManager.instance.getPlanOfProject(projectPlanId, projectId); + if (p.getInteger("flowStatus") == ProjectPlanConfigService.FLOW_STATUS_PROCESSING) { + throw new DataSpecificationException(Language.L("该任务面板不可新建任务")); + } + + record.setLong("taskNumber", getNextTaskNumber(projectId)); + applyFlowStatus(record); + record.setInt("seq", getNextSeqViaMidValue(projectPlanId)); record = super.create(record); @@ -68,17 +76,26 @@ public class ProjectTaskService extends BaseTaskService { checkInMembers(user, record.getPrimary()); // 自动完成 - int flowStatus = applyFlowStatue(record); + int newFlowStatus = applyFlowStatus(record); - if (flowStatus == ProjectPlanConfigService.FLOW_STATUS_END) { + if (newFlowStatus == ProjectPlanConfigService.FLOW_STATUS_END) { record.setDate("endTime", CalendarUtils.now()); } else if (record.hasValue("status")) { int status = record.getInt("status"); + // 处理完成时间 if (status == 0) { record.setNull("endTime"); record.setInt("seq", getSeqInStatus(record.getPrimary(), false)); } else { + + // 检查工作流 + Object[] flowStatus = Application.getQueryFactory() + .uniqueNoFilter(record.getPrimary(), "projectPlanId.flowStatus"); + if ((int) flowStatus[0] == ProjectPlanConfigService.FLOW_STATUS_PROCESSING) { + throw new DataSpecificationException(Language.L("该任务面板不可完成任务")); + } + record.setDate("endTime", CalendarUtils.now()); record.setInt("seq", getSeqInStatus(record.getPrimary(), true)); } @@ -141,13 +158,13 @@ public class ProjectTaskService extends BaseTaskService { */ synchronized private int getSeqInStatus(ID taskId, boolean desc) { - Object[] taskStatus = Application.createQuery( + Object[] taskStatus = Application.createQueryNoFilter( "select status,projectPlanId from ProjectTask where taskId = ?") .setParameter(1, taskId) .unique(); if (taskStatus == null) return 1; - Object[] seq = Application.createQuery( + Object[] seq = Application.createQueryNoFilter( "select " + (desc ? "max" : "min") + "(seq) from ProjectTask where status = ? and projectPlanId = ?") .setParameter(1, taskStatus[0]) .setParameter(2, taskStatus[1]) @@ -162,7 +179,7 @@ public class ProjectTaskService extends BaseTaskService { * * @param newOrUpdate */ - private int applyFlowStatue(Record newOrUpdate) { + private int applyFlowStatus(Record newOrUpdate) { if (newOrUpdate.hasValue("projectPlanId")) { ConfigBean c = ProjectManager.instance.getPlanOfProject( newOrUpdate.getID("projectPlanId"), newOrUpdate.getID("projectId")); @@ -178,11 +195,10 @@ public class ProjectTaskService extends BaseTaskService { return -1; } - /** - * @param taskId - */ private void sendNotification(ID taskId) { Object[] task = Application.getQueryFactory().uniqueNoFilter(taskId, "executor", "taskName"); + if (task[0] == null) return; + String msg = Language.L("有一个新任务分派给你") + " \n> " + task[1]; Application.getNotifications().send( MessageBuilder.createMessage((ID) task[0], msg, Message.TYPE_PROJECT, taskId)); diff --git a/src/main/java/com/rebuild/core/service/query/QueryFactory.java b/src/main/java/com/rebuild/core/service/query/QueryFactory.java index b4ac70210..5574b936f 100644 --- a/src/main/java/com/rebuild/core/service/query/QueryFactory.java +++ b/src/main/java/com/rebuild/core/service/query/QueryFactory.java @@ -40,6 +40,7 @@ public class QueryFactory { /** * @param ajql * @return + * @see #createQueryNoFilter(String) */ public Query createQuery(String ajql) { return createQuery(ajql, UserContextHolder.getUser()); @@ -54,14 +55,6 @@ public class QueryFactory { return createQuery(ajql, Application.getPrivilegesManager().createQueryFilter(user)); } - /** - * @param ajql - * @return - */ - public Query createQueryNoFilter(String ajql) { - return createQuery(ajql, RoleBaseQueryFilter.ALLOWED); - } - /** * @param ajql * @param filter @@ -76,11 +69,25 @@ public class QueryFactory { } /** - * @param sql + * 1.无权限实体查询(无权限实体使用 #createQuery 除管理员外将查不到数据) + * 2.有权限实体查询单不应用角色(权限) + * + * @param ajql + * @return + * @see #createQuery(String) + */ + public Query createQueryNoFilter(String ajql) { + return createQuery(ajql, RoleBaseQueryFilter.ALLOWED); + } + + /** + * 原生 SQL 查询 + * + * @param rawSql * @return */ - public NativeQuery createNativeQuery(String sql) { - return aPMFactory.createNativeQuery(sql) + public NativeQuery createNativeQuery(String rawSql) { + return aPMFactory.createNativeQuery(rawSql) .setTimeout(QUERY_TIMEOUT) .setSlowLoggerTime(SLOW_LOGGER_TIME); } @@ -129,11 +136,6 @@ public class QueryFactory { return createQueryNoFilter(sql).setParameter(1, recordId).unique(); } - /** - * @param recordId - * @param fields - * @return - */ private String buildUniqueSql(ID recordId, String... fields) { Assert.notNull(recordId, "[recordId] cannot be null"); diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/FieldWriteback.java b/src/main/java/com/rebuild/core/service/trigger/impl/FieldWriteback.java index 86be444f0..6b777d2af 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/FieldWriteback.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/FieldWriteback.java @@ -117,7 +117,7 @@ public class FieldWriteback extends FieldAggregation { } else { String sql = String.format("select %s from %s where %s = ?", targetEntity.getPrimaryField().getName(), targetFieldEntity[1], targetFieldEntity[0]); - Object[][] array = Application.getQueryFactory().createQueryNoFilter(sql) + Object[][] array = Application.createQueryNoFilter(sql) .setParameter(1, operatingContext.getAnyRecord().getPrimary()) .array(); diff --git a/src/main/java/com/rebuild/core/support/general/BatchOperatorQuery.java b/src/main/java/com/rebuild/core/support/general/BatchOperatorQuery.java index bfce830c2..6b19c6c93 100644 --- a/src/main/java/com/rebuild/core/support/general/BatchOperatorQuery.java +++ b/src/main/java/com/rebuild/core/support/general/BatchOperatorQuery.java @@ -127,7 +127,7 @@ public class BatchOperatorQuery extends SetUser { int pageNo = queryData.getIntValue("pageNo"); int pageSize = queryData.getIntValue("pageSize"); - Object[][] array = Application.getQueryFactory().createQuery(sql, getUser()) + Object[][] array = Application.createQuery(sql, getUser()) .setLimit(pageSize, pageNo * pageSize - pageSize) .setTimeout(60) .array(); diff --git a/src/main/java/com/rebuild/core/support/general/DataListBuilderImpl.java b/src/main/java/com/rebuild/core/support/general/DataListBuilderImpl.java index 974431621..a5ce7418f 100644 --- a/src/main/java/com/rebuild/core/support/general/DataListBuilderImpl.java +++ b/src/main/java/com/rebuild/core/support/general/DataListBuilderImpl.java @@ -66,11 +66,11 @@ public class DataListBuilderImpl implements DataListBuilder { public JSON getJSONResult() { int totalRows = 0; if (queryParser.isNeedReload()) { - Object[] count = Application.getQueryFactory().createQuery(queryParser.toCountSql(), user).unique(); + Object[] count = Application.createQuery(queryParser.toCountSql(), user).unique(); totalRows = ObjectUtils.toInt(count[0]); } - Query query = Application.getQueryFactory().createQuery(queryParser.toSql(), user); + Query query = Application.createQuery(queryParser.toSql(), user); int[] limits = queryParser.getSqlLimit(); Object[][] data = query.setLimit(limits[0], limits[1]).array(); diff --git a/src/main/java/com/rebuild/utils/AppUtils.java b/src/main/java/com/rebuild/utils/AppUtils.java index b011fddb3..66f3f341e 100644 --- a/src/main/java/com/rebuild/utils/AppUtils.java +++ b/src/main/java/com/rebuild/utils/AppUtils.java @@ -43,6 +43,10 @@ public class AppUtils { public static final String SK_LOCALE = WebUtils.KEY_PREFIX + ".LOCALE"; public static final String CK_LOCALE = "rb.locale"; + // RbMob + public static final String HF_CLIENT = "X-Client"; + public static final String HF_LOCALE = "X-ClientLocale"; + /** * @return * @see BootApplication#getContextPath() @@ -106,6 +110,9 @@ public class AppUtils { */ public static String getReuqestLocale(HttpServletRequest request) { String locale = (String) ServletUtils.getSessionAttribute(request, SK_LOCALE); + if (locale == null) { + locale = StringUtils.defaultIfBlank(request.getHeader(HF_LOCALE), null); + } if (locale == null) { locale = RebuildConfiguration.get(ConfigurationItem.DefaultLanguage); } @@ -171,7 +178,7 @@ public class AppUtils { * @return */ public static boolean isRbMobile(HttpServletRequest request) { - String UA = request.getHeader("X-Client"); + String UA = request.getHeader(HF_CLIENT); return UA != null && UA.startsWith("RB/Mobile-"); } diff --git a/src/main/java/com/rebuild/utils/HttpUtils.java b/src/main/java/com/rebuild/utils/HttpUtils.java index 70ab97fc6..c597fd6b1 100644 --- a/src/main/java/com/rebuild/utils/HttpUtils.java +++ b/src/main/java/com/rebuild/utils/HttpUtils.java @@ -133,7 +133,7 @@ public class HttpUtils { * @throws IOException */ public static File readBinary(String url) throws IOException { - File tmp = RebuildConfiguration.getFileOfTemp("download." + UUID.randomUUID().toString()); + File tmp = RebuildConfiguration.getFileOfTemp("download." + UUID.randomUUID()); boolean success = readBinary(url, tmp, Collections.singletonMap(HttpHeaders.USER_AGENT, RB_UA)); return success && tmp.exists() ? tmp : null; } diff --git a/src/main/java/com/rebuild/web/RebuildWebConfigurer.java b/src/main/java/com/rebuild/web/RebuildWebConfigurer.java index f0ced978f..f5616c3b3 100644 --- a/src/main/java/com/rebuild/web/RebuildWebConfigurer.java +++ b/src/main/java/com/rebuild/web/RebuildWebConfigurer.java @@ -32,6 +32,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.thymeleaf.exceptions.TemplateInputException; @@ -92,9 +93,16 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver .excludePathPatterns("/gw/api/**") .excludePathPatterns("/language/**") .excludePathPatterns("/assets/**") + .excludePathPatterns("/h5app/**") .excludePathPatterns("/*.txt"); } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/h5app/**") + .addResourceLocations("classpath:/public/h5app/"); + } + @Override public void addArgumentResolvers(List resolvers) { // URL 参数 @@ -113,11 +121,6 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver return createError(request, (Exception) request.getAttribute(ServletUtils.ERROR_EXCEPTION), status, model); } - /** - * @param request - * @param ex - * @return - */ private ModelAndView createError(HttpServletRequest request, Exception ex, HttpStatus status, Map model) { // IGNORED if (request.getRequestURI().contains("/assets/")) return null; @@ -164,12 +167,12 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver } /** - * 获取请求/引用地址 + * 获取请求+引用地址 * * @param request * @return */ - static String getRequestUrls(HttpServletRequest request) { + protected static String getRequestUrls(HttpServletRequest request) { String reqUrl = request.getRequestURL().toString(); String refUrl = ServletUtils.getReferer(request); diff --git a/src/main/java/com/rebuild/web/RebuildWebInterceptor.java b/src/main/java/com/rebuild/web/RebuildWebInterceptor.java index 6ecb7973f..ffad86100 100644 --- a/src/main/java/com/rebuild/web/RebuildWebInterceptor.java +++ b/src/main/java/com/rebuild/web/RebuildWebInterceptor.java @@ -154,7 +154,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt if (requestEntry.isHtmlRequest()) { sendRedirect(response, "/user/login", requestUri); } else { - ServletUtils.writeJson(response, RespBody.error(HttpStatus.FORBIDDEN.value()).toJSONString()); + ServletUtils.writeJson(response, RespBody.error(HttpStatus.UNAUTHORIZED.value()).toJSONString()); } return false; @@ -189,43 +189,45 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt * @param request * @param response * @return + * @see AppUtils#getReuqestLocale(HttpServletRequest) */ private String detectLocale(HttpServletRequest request, HttpServletResponse response) { + String rbmobLocale = request.getHeader(AppUtils.HF_LOCALE); + if (rbmobLocale != null) return rbmobLocale; + // 0. Session - String locale = (String) ServletUtils.getSessionAttribute(request, AppUtils.SK_LOCALE); + String havingLocale = (String) ServletUtils.getSessionAttribute(request, AppUtils.SK_LOCALE); String urlLocale = request.getParameter("locale"); - if (StringUtils.isNotBlank(urlLocale) && !urlLocale.equals(locale)) { + if (StringUtils.isNotBlank(urlLocale) && !urlLocale.equals(havingLocale)) { urlLocale = Application.getLanguage().available(urlLocale); if (urlLocale != null) { - locale = urlLocale; + havingLocale = urlLocale; - ServletUtils.setSessionAttribute(request, AppUtils.SK_LOCALE, locale); - ServletUtils.addCookie(response, AppUtils.CK_LOCALE, locale, + ServletUtils.setSessionAttribute(request, AppUtils.SK_LOCALE, havingLocale); + ServletUtils.addCookie(response, AppUtils.CK_LOCALE, havingLocale, CommonsCache.TS_DAY * 90, null, StringUtils.defaultIfBlank(AppUtils.getContextPath(), "/")); - if (Application.devMode()) { - Application.getLanguage().refresh(); - } + if (Application.devMode()) Application.getLanguage().refresh(); } } - if (locale != null) return locale; + if (havingLocale != null) return havingLocale; // 1. Cookie - locale = ServletUtils.readCookie(request, AppUtils.CK_LOCALE); - if (locale == null) { + havingLocale = ServletUtils.readCookie(request, AppUtils.CK_LOCALE); + if (havingLocale == null) { // 2. User-Local - locale = request.getLocale().toString(); + havingLocale = request.getLocale().toString(); } // 3. Default - if ((locale = Application.getLanguage().available(locale)) == null) { - locale = RebuildConfiguration.get(ConfigurationItem.DefaultLanguage); + if ((havingLocale = Application.getLanguage().available(havingLocale)) == null) { + havingLocale = RebuildConfiguration.get(ConfigurationItem.DefaultLanguage); } - ServletUtils.setSessionAttribute(request, AppUtils.SK_LOCALE, locale); - return locale; + ServletUtils.setSessionAttribute(request, AppUtils.SK_LOCALE, havingLocale); + return havingLocale; } /** diff --git a/src/main/java/com/rebuild/web/project/ProjectController.java b/src/main/java/com/rebuild/web/project/ProjectController.java index 9a97c5c35..d432ca929 100644 --- a/src/main/java/com/rebuild/web/project/ProjectController.java +++ b/src/main/java/com/rebuild/web/project/ProjectController.java @@ -11,18 +11,21 @@ import cn.devezhao.commons.CodecUtils; import cn.devezhao.commons.ObjectUtils; import cn.devezhao.persist4j.engine.ID; import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.rebuild.api.RespBody; import com.rebuild.core.Application; import com.rebuild.core.configuration.ConfigBean; import com.rebuild.core.configuration.ConfigurationException; import com.rebuild.core.service.project.ProjectManager; import com.rebuild.core.support.i18n.Language; import com.rebuild.utils.AppUtils; +import com.rebuild.utils.JSONUtils; import com.rebuild.web.BaseController; import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; @@ -37,7 +40,7 @@ import java.util.regex.Pattern; * @author devezhao * @since 2020/6/29 */ -@Controller +@RestController @RequestMapping("/project/") public class ProjectController extends BaseController { @@ -80,6 +83,31 @@ public class ProjectController extends BaseController { return mv; } + @GetMapping("{projectId}/details") + public RespBody getPlans(@PathVariable String projectId, HttpServletRequest request) throws IOException { + final ID user = getRequestUser(request); + + JSONObject details; + try { + ConfigBean p = ProjectManager.instance.getProject(ID.valueOf(projectId), user); + details = JSONUtils.toJSONObject( + new String[] { "projectName", "isMember" }, + new Object[] { p.getString("projectName"), p.get("members", Set.class).contains(user) }); + + } catch (ConfigurationException ex) { + return RespBody.error(ex.getLocalizedMessage(), 403); + } + + ConfigBean[] plans = ProjectManager.instance.getPlansOfProject(ID.valueOf(projectId)); + JSONArray array = new JSONArray(); + for (ConfigBean cb : plans) { + array.add(cb.toJSON()); + } + details.put("projectPlans", array); + + return RespBody.ok(details); + } + /** * @see com.rebuild.web.general.ListAndViewRedirection */ diff --git a/src/main/java/com/rebuild/web/project/ProjectTaskController.java b/src/main/java/com/rebuild/web/project/ProjectTaskController.java index e0c7ae78b..157c7d4e2 100644 --- a/src/main/java/com/rebuild/web/project/ProjectTaskController.java +++ b/src/main/java/com/rebuild/web/project/ProjectTaskController.java @@ -70,18 +70,18 @@ public class ProjectTaskController extends BaseController { return null; } - ConfigBean cfg = ProjectManager.instance.getProjectByTask(taskId2, user); + ConfigBean project = ProjectManager.instance.getProjectByTask(taskId2, user); ModelAndView mv = createModelAndView("/project/task-view"); mv.getModel().put("id", taskId2.toLiteral()); - mv.getModel().put("projectIcon", cfg.getString("iconName")); - mv.getModel().put("isMember", cfg.get("members", Set.class).contains(user)); - mv.getModel().put("isManageable", ProjectHelper.isManageable(taskId2, user)); + mv.getModel().put("projectIcon", project.getString("iconName")); + mv.getModel().put("isMember", project.get("members", Set.class).contains(user)); return mv; } @RequestMapping("tasks/list") public JSON taskList(@IdParam(name = "plan") ID planId, HttpServletRequest request) { + final ID user = getRequestUser(request); String queryWhere = "projectPlanId = ?"; // 关键词搜索 @@ -125,7 +125,7 @@ public class ProjectTaskController extends BaseController { JSONArray alist = new JSONArray(); for (Object[] o : tasks) { - alist.add(formatTask(o, true)); + alist.add(formatTask(o, user)); } return JSONUtils.toJSONObject( @@ -139,19 +139,19 @@ public class ProjectTaskController extends BaseController { "select " + BASE_FIELDS + " from ProjectTask where taskId = ?") .setParameter(1, taskId) .unique(); - return formatTask(task, true); + return formatTask(task, null); } @GetMapping("tasks/details") - public JSON taskDetails(@IdParam(name = "task") ID taskId) { + public JSON taskDetails(@IdParam(name = "task") ID taskId, HttpServletRequest request) { + final ID user = getRequestUser(request); + Object[] task = Application.createQueryNoFilter( - "select " + BASE_FIELDS + ",projectId,description,attachments,relatedRecord from ProjectTask where taskId = ?") + "select " + BASE_FIELDS + ",description,attachments,relatedRecord from ProjectTask where taskId = ?") .setParameter(1, taskId) .unique(); - JSONObject details = formatTask(task, true); + JSONObject details = formatTask(task, user); - // 状态面板 - details.put("projectId", task[11]); details.put("description", task[12]); String attachments = (String) task[13]; details.put("attachments", JSON.parseArray(attachments)); @@ -168,24 +168,33 @@ public class ProjectTaskController extends BaseController { } private static final String BASE_FIELDS = - "projectId.projectCode,taskNumber,taskId,taskName,createdOn,deadline,executor,status,seq,priority,endTime"; + "projectId,projectPlanId,taskNumber,taskId,taskName,createdOn,deadline,executor,status,seq,priority,endTime"; - private JSONObject formatTask(Object[] o, boolean appendTags) { - String taskNumber = o[1].toString(); - if (StringUtils.isNotBlank((String) o[0])) taskNumber = o[0] + "-" + taskNumber; + private JSONObject formatTask(Object[] o, ID user) { + final ConfigBean project = ProjectManager.instance.getProject((ID) o[0], user); - String createdOn = I18nUtils.formatDate((Date) o[4]); - String deadline = I18nUtils.formatDate((Date) o[5]); - String endTime = I18nUtils.formatDate((Date) o[10]); + String taskNumber = String.format("%s-%s", project.getString("projectCode"), o[2]); + String createdOn = I18nUtils.formatDate((Date) o[5]); + String deadline = I18nUtils.formatDate((Date) o[6]); + String endTime = I18nUtils.formatDate((Date) o[11]); - Object[] executor = o[6] == null ? null : new Object[]{o[6], UserHelper.getName((ID) o[6])}; + Object[] executor = o[7] == null ? null : new Object[]{o[7], UserHelper.getName((ID) o[7])}; JSONObject data = JSONUtils.toJSONObject( - new String[] { "id", "taskNumber", "taskName", "createdOn", "deadline", "executor", "status", "seq", "priority", "endTime" }, - new Object[] { o[2], taskNumber, o[3], createdOn, deadline, executor, o[7], o[8], o[9], endTime }); + new String[] { "id", "taskNumber", "taskName", "createdOn", "deadline", "executor", "status", "seq", "priority", "endTime", "projectId" }, + new Object[] { o[3], taskNumber, o[4], createdOn, deadline, executor, o[8], o[9], o[10], endTime, o[0] }); + // 标签 + data.put("tags", TaskTagController.getTaskTags((ID) o[3])); - if (appendTags) { - data.put("tags", TaskTagController.getTaskTags((ID) o[2])); + if (user != null) { + // 项目信息 + ConfigBean plan = ProjectManager.instance.getPlanOfProject((ID) o[1], (ID) o[0]); + data.put("planName", String.format("%s (%s)", + project.getString("projectName"), plan.getString("planName"))); + data.put("planFlow", plan.getInteger("flowStatus")); + // 权限 + data.put("projectMember", project.get("members", Set.class).contains(user)); + data.put("isManageable", ProjectHelper.isManageable((ID) o[3], user)); } return data; @@ -199,8 +208,7 @@ public class ProjectTaskController extends BaseController { return sort; } - // -- for EntityView - + // for View of Entity @GetMapping("alist") public RespBody getProjectAndPlans(HttpServletRequest request) { final ID user = getRequestUser(request); @@ -230,6 +238,8 @@ public class ProjectTaskController extends BaseController { @IdParam(name = "task", required = false) ID taskId, HttpServletRequest request) { Assert.isTrue(relatedId != null || taskId != null, Language.L("无效请求参数")); + + final ID user = getRequestUser(request); String queryWhere = String.format("relatedRecord = '%s'", relatedId); // 关键词搜索 @@ -248,30 +258,16 @@ public class ProjectTaskController extends BaseController { queryWhere = String.format("taskId = '%s'", taskId); } - String querySql = "select " + BASE_FIELDS + ",projectPlanId,projectId from ProjectTask where " + queryWhere; + String querySql = "select " + BASE_FIELDS + " from ProjectTask where " + queryWhere; Object[][] tasks = Application.createQueryNoFilter(querySql) .setLimit(pageSize, pageNo * pageSize - pageSize) .array(); - JSONArray alist = new JSONArray(); + JSONArray array = new JSONArray(); for (Object[] o : tasks) { - JSONObject formatted = formatTask(o, false); - formatted.put("taskNumber", String.format("%s-%d", o[0], o[1] )); - - Object executor = o[6] == null ? null : new Object[] { o[6], UserHelper.getName((ID) o[6]) }; - formatted.put("executor", executor); - - ID projectPlanId = (ID) o[11]; - ID projectId = (ID) o[12]; - ConfigBean project = ProjectManager.instance.getProject(projectId, null); - ConfigBean plan = ProjectManager.instance.getPlanOfProject(projectPlanId, projectId); - formatted.put("planName", String.format("%s (%s)", - project.getString("projectName"), plan.getString("planName"))); - formatted.put("planFlow", plan.getInteger("flowStatus")); - - alist.add(formatted); + array.add(formatTask(o, user)); } - return alist; + return array; } } diff --git a/src/main/java/com/rebuild/web/project/TaskTagController.java b/src/main/java/com/rebuild/web/project/TaskTagController.java index e724b16d4..d4b943b30 100644 --- a/src/main/java/com/rebuild/web/project/TaskTagController.java +++ b/src/main/java/com/rebuild/web/project/TaskTagController.java @@ -106,9 +106,9 @@ public class TaskTagController extends BaseController { */ static JSONArray getTaskTags(ID taskId) { Object[][] tags = Application.createQueryNoFilter( - "select tagId.tagName,tagId.color,relationId from ProjectTaskTagRelation where taskId = ? order by createdOn") + "select tagId.tagName,tagId.color,relationId,tagId from ProjectTaskTagRelation where taskId = ? order by createdOn") .setParameter(1, taskId) .array(); - return JSONUtils.toJSONObjectArray(new String[] { "name", "color" , "rid" }, tags); + return JSONUtils.toJSONObjectArray(new String[] { "name", "color" , "rid", "id" }, tags); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index af7357a64..61b40d975 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,7 +24,7 @@ spring: multipart.enabled: false web: resources: - static-locations: classpath:/web/assets + static-locations: classpath:/web/assets/ mvc: static-path-pattern: /assets/** thymeleaf: diff --git a/src/main/resources/i18n/lang.zh_CN.json b/src/main/resources/i18n/lang.zh_CN.json index 35d42affb..96b9e5029 100644 --- a/src/main/resources/i18n/lang.zh_CN.json +++ b/src/main/resources/i18n/lang.zh_CN.json @@ -1157,7 +1157,7 @@ "注册信息已提交,审批结果将通过邮件通知你":"注册信息已提交,审批结果将通过邮件通知你", "注册成功":"注册成功", "注册验证码":"注册验证码", - "注销登录":"注销登录", + "退出":"退出", "流程结束":"流程结束", "流程设计":"流程设计", "测试发送":"测试发送", diff --git a/src/main/resources/public/h5app/index.html b/src/main/resources/public/h5app/index.html new file mode 100644 index 000000000..5cd0a0453 --- /dev/null +++ b/src/main/resources/public/h5app/index.html @@ -0,0 +1,11 @@ + + + + + + REBUILD + + + Free version does not support H5 App feature. (View details) + + diff --git a/src/main/resources/web/_include/nav-top.html b/src/main/resources/web/_include/nav-top.html index 224a9fc18..4ff81d288 100644 --- a/src/main/resources/web/_include/nav-top.html +++ b/src/main/resources/web/_include/nav-top.html @@ -29,7 +29,7 @@
[[${user.getEmail() ?: bundle.L('邮箱未设置')}]]
[[${bundle.L('个人设置')}]] - [[${bundle.L('注销登录')}]] + [[${bundle.L('退出')}]]
[[${bundle.L('选择主题')}]]
    diff --git a/src/main/resources/web/assets/css/rb-base.css b/src/main/resources/web/assets/css/rb-base.css index da021af32..043233c2b 100644 --- a/src/main/resources/web/assets/css/rb-base.css +++ b/src/main/resources/web/assets/css/rb-base.css @@ -11364,20 +11364,14 @@ canvas { color: #404040; border-radius: 99px; background: #f7f7f7; - background: rgba(255, 255, 255, 0.8); width: 320px; border-width: 0; padding: 4px 15px; text-align: left; - transition: background 0.2s; -} - -.rb-top-header .search-input-gs:focus { - background: rgba(255, 255, 255, 0.98); } .rb-top-header .search-input-gs::placeholder { - color: rgba(0, 0, 0, 0.4); + color: #aaa; text-align: center; } diff --git a/src/main/resources/web/assets/css/rb-page.css b/src/main/resources/web/assets/css/rb-page.css index 4d1d7f71c..4547d3eea 100644 --- a/src/main/resources/web/assets/css/rb-page.css +++ b/src/main/resources/web/assets/css/rb-page.css @@ -3781,11 +3781,11 @@ a.select-lang:hover { } .use-theme ul li a.theme-dark { - background-color: #112034 !important; + background-color: #2d333b !important; } .use-theme ul li a.theme-blue { - background-color: #4285f4 !important; + background-color: #4873c0 !important; } .use-theme ul li a.theme-green { diff --git a/src/main/resources/web/assets/js/project/task-view.js b/src/main/resources/web/assets/js/project/task-view.js index 416a16a31..0296a24d2 100644 --- a/src/main/resources/web/assets/js/project/task-view.js +++ b/src/main/resources/web/assets/js/project/task-view.js @@ -18,7 +18,7 @@ let __TaskContent let __TaskComment $(document).ready(() => { - renderRbcomp(, 'task-contents', function () { + renderRbcomp(, 'task-contents', function () { __TaskContent = this }) if (wpc.isMember) { @@ -45,7 +45,7 @@ class TaskForm extends React.Component {
    - {this.props.editable && this.props.isManageable && ( + {this.props.editable && this.state.isManageable && (
    @@ -256,7 +256,7 @@ class ValueStatus extends ValueComp {
    @@ -644,7 +644,7 @@ class ValueTags extends ValueComp {
    ) : ( - tags.length === 0 &&
    {$L('无')}
    + tags.length === 0 && {$L('无')} )}
    ) diff --git a/src/main/resources/web/assets/js/rb-approval.js b/src/main/resources/web/assets/js/rb-approval.js index ce46ba6b1..e6d517465 100644 --- a/src/main/resources/web/assets/js/rb-approval.js +++ b/src/main/resources/web/assets/js/rb-approval.js @@ -234,9 +234,11 @@ class ApprovalUsersForm extends RbFormHandler { )}
) : ( -
- -
+ !this.state.isLastStep && ( +
+ +
+ ) )} {ccHas && (
@@ -505,7 +507,7 @@ class ApprovalStepViewer extends React.Component { {!this.state.steps && }