mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 15:35:55 +08:00
分配/共享
This commit is contained in:
parent
f9d15cbd21
commit
d5eec8fee4
|
@ -35,6 +35,7 @@ import com.rebuild.server.query.QueryFactory;
|
|||
import com.rebuild.server.service.CommonService;
|
||||
import com.rebuild.server.service.SQLExecutor;
|
||||
import com.rebuild.server.service.base.GeneralEntityService;
|
||||
import com.rebuild.server.service.notification.NotificationService;
|
||||
import com.rebuild.web.OnlineSessionStore;
|
||||
|
||||
import cn.devezhao.persist4j.PersistManagerFactory;
|
||||
|
@ -158,11 +159,11 @@ public final class Application {
|
|||
public static SQLExecutor getSQLExecutor() {
|
||||
return getBean(SQLExecutor.class);
|
||||
}
|
||||
|
||||
public static NotificationService getNotifications() {
|
||||
return getBean(NotificationService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see Application#getGeneralEntityService(int)
|
||||
*/
|
||||
public static CommonService getCommonService() {
|
||||
return getBean(CommonService.class);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class DepartmentService extends GeneralEntityService {
|
|||
@Override
|
||||
public Record createOrUpdate(Record record) {
|
||||
record = super.createOrUpdate(record);
|
||||
Application.getUserStore().refreshDept(record.getPrimary());
|
||||
Application.getUserStore().refreshDepartment(record.getPrimary());
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ import java.security.Guard;
|
|||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
import com.rebuild.server.metadata.EntityHelper;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.server.service.base.BulkContext;
|
||||
import com.rebuild.server.service.base.GeneralEntityService;
|
||||
|
||||
import cn.devezhao.bizz.privileges.Permission;
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
|
@ -59,62 +59,55 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
|||
return;
|
||||
}
|
||||
|
||||
Object idOrRecord = invocation.getArguments()[0];
|
||||
|
||||
ID recordId = null;
|
||||
Entity entity = null;
|
||||
ID caller = null;
|
||||
|
||||
if (idOrRecord instanceof Record) {
|
||||
recordId = ((Record) idOrRecord).getPrimary();
|
||||
entity = ((Record) idOrRecord).getEntity();
|
||||
caller = ((Record) idOrRecord).getEditor();
|
||||
} else if (idOrRecord instanceof ID) {
|
||||
recordId = (ID) idOrRecord;
|
||||
entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
} else if (idOrRecord instanceof ID[]) {
|
||||
Assert.isTrue(((ID[]) idOrRecord).length > 1, "Bulk must have at least 2 IDs");
|
||||
entity = MetadataHelper.getEntity(((ID[]) idOrRecord)[0].getEntityCode());
|
||||
} else {
|
||||
throw new IllegalArgumentException("First arguments must be Record/ID/ID[]");
|
||||
}
|
||||
|
||||
// 无权限字段的不检查
|
||||
if (!EntityHelper.hasPrivilegesField(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (caller == null) {
|
||||
// 当前会话用户
|
||||
caller = Application.currentCallerUser();
|
||||
}
|
||||
|
||||
boolean isBulk = invocation.getMethod().getName().startsWith("bulkDelete");
|
||||
boolean isBulk = invocation.getMethod().getName().startsWith("bulk");
|
||||
if (isBulk) {
|
||||
boolean deleteAllowed = Application.getSecurityManager().allowed(caller, entity.getEntityCode(), BizzPermission.DELETE);
|
||||
if (!deleteAllowed) {
|
||||
Object first = invocation.getArguments()[0];
|
||||
if (!(first instanceof BulkContext)) {
|
||||
throw new IllegalArgumentException("First argument must be BulkContext");
|
||||
}
|
||||
|
||||
BulkContext context = (BulkContext) first;
|
||||
ID caller = Application.currentCallerUser();
|
||||
|
||||
if (!Application.getSecurityManager().allowed(caller, context.getMainEntity().getEntityCode(), context.getAction())) {
|
||||
throw new AccessDeniedException(
|
||||
"User [ " + caller + " ] not allowed execute action [ " + BizzPermission.DELETE + " ]. Entity : " + entity.getName());
|
||||
"User [ " + caller + " ] not allowed execute action [ " + context.getAction() + " ]. Entity : " + context.getMainEntity());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final Permission permission = getPermissionByMethod(invocation.getMethod(), recordId == null);
|
||||
Object idOrRecord = invocation.getArguments()[0];
|
||||
|
||||
ID recordId = null;
|
||||
Entity entity = null;
|
||||
|
||||
if (idOrRecord instanceof Record) {
|
||||
recordId = ((Record) idOrRecord).getPrimary();
|
||||
entity = ((Record) idOrRecord).getEntity();
|
||||
} else if (idOrRecord instanceof ID) {
|
||||
recordId = (ID) idOrRecord;
|
||||
entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
} else {
|
||||
throw new IllegalArgumentException("First argument must be Record/ID");
|
||||
}
|
||||
|
||||
ID caller = Application.currentCallerUser();
|
||||
Permission action = getPermissionByMethod(invocation.getMethod(), recordId == null);
|
||||
|
||||
boolean isAllowed = false;
|
||||
if (permission == BizzPermission.CREATE) {
|
||||
isAllowed = Application.getSecurityManager().allowed(caller, entity.getEntityCode(), permission);
|
||||
if (action == BizzPermission.CREATE) {
|
||||
isAllowed = Application.getSecurityManager().allowed(caller, entity.getEntityCode(), action);
|
||||
} else {
|
||||
if (recordId == null) {
|
||||
throw new IllegalArgumentException("No primary in record!");
|
||||
}
|
||||
|
||||
isAllowed = Application.getSecurityManager().allowed(caller, recordId, permission);
|
||||
isAllowed = Application.getSecurityManager().allowed(caller, recordId, action);
|
||||
}
|
||||
|
||||
if (!isAllowed) {
|
||||
throw new AccessDeniedException(
|
||||
"User [ " + caller + " ] not allowed execute action [ " + permission.getName() + " ]. "
|
||||
+ (recordId == null ? "Entity : " + entity.getName() : "Record : " + recordId));
|
||||
"User [ " + caller + " ] not allowed execute action [ " + action + " ]. " + (recordId == null ? "Entity : " + entity : "Record : " + recordId));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,9 +138,14 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
|||
* @return
|
||||
*/
|
||||
protected boolean isNeedCheck(MethodInvocation invocation) {
|
||||
// 仅 GeneralEntityService 或其子类
|
||||
if (!GeneralEntityService.class.isAssignableFrom(invocation.getThis().getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String act = invocation.getMethod().getName();
|
||||
return act.startsWith("create") || act.startsWith("update")
|
||||
|| act.startsWith("delete") || act.startsWith("assign") || act.startsWith("share")
|
||||
|| act.startsWith("bulkDelete");
|
||||
return act.startsWith("create") || act.startsWith("update") || act.startsWith("delete")
|
||||
|| act.startsWith("assign") || act.startsWith("share")
|
||||
|| act.startsWith("bulk");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.bizz.privileges;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -156,6 +159,41 @@ public class UserStore {
|
|||
return USERs.values().toArray(new User[USERs.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deptId
|
||||
* @return
|
||||
* @throws NoMemberFoundException
|
||||
*/
|
||||
public Department getDepartment(ID deptId) throws NoMemberFoundException {
|
||||
Department b = DEPTs.get(deptId);
|
||||
if (b == null) {
|
||||
throw new NoMemberFoundException("No Department found: " + deptId);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Department[] getAllDepartments() {
|
||||
return DEPTs.values().toArray(new Department[DEPTs.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一级部门列表
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Department[] getTopDepartments() {
|
||||
List<Department> top = new ArrayList<>();
|
||||
for (Department dept : DEPTs.values()) {
|
||||
if (dept.getParent() == null) {
|
||||
top.add(dept);
|
||||
}
|
||||
}
|
||||
return top.toArray(new Department[top.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param roleId
|
||||
* @return
|
||||
|
@ -169,19 +207,6 @@ public class UserStore {
|
|||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deptId
|
||||
* @return
|
||||
* @throws NoMemberFoundException
|
||||
*/
|
||||
public Department getDept(ID deptId) throws NoMemberFoundException {
|
||||
Department b = DEPTs.get(deptId);
|
||||
if (b == null) {
|
||||
throw new NoMemberFoundException("No Department found: " + deptId);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新用户缓存
|
||||
*
|
||||
|
@ -230,7 +255,7 @@ public class UserStore {
|
|||
|
||||
ID newDeptId = (ID) u[6];
|
||||
if (oldDept == null || !oldDept.getIdentity().equals(newDeptId)) {
|
||||
getDept(newDeptId).addMember(newUser);
|
||||
getDepartment(newDeptId).addMember(newUser);
|
||||
} else {
|
||||
oldDept.addMember(newUser);
|
||||
}
|
||||
|
@ -259,7 +284,6 @@ public class UserStore {
|
|||
role.addPrivileges(priv);
|
||||
}
|
||||
}
|
||||
|
||||
store(role);
|
||||
return;
|
||||
}
|
||||
|
@ -273,7 +297,7 @@ public class UserStore {
|
|||
*
|
||||
* @param deptId
|
||||
*/
|
||||
public void refreshDept(ID deptId) {
|
||||
public void refreshDepartment(ID deptId) {
|
||||
Department oldDept = DEPTs.get(deptId);
|
||||
|
||||
Object[] o = aPMFactory.createQuery(
|
||||
|
@ -286,16 +310,16 @@ public class UserStore {
|
|||
ID parent = (ID) o[2];
|
||||
if (oldDept != null) {
|
||||
if (oldDept.getParent() == null && parent != null) { // 新加入了部门
|
||||
getDept(parent).addChild(newDept);
|
||||
getDepartment(parent).addChild(newDept);
|
||||
} else if (oldDept.getParent() != null && parent == null) { // 离开了部门
|
||||
getDept(parent).removeMember(oldDept);
|
||||
getDepartment(parent).removeMember(oldDept);
|
||||
} else if (oldDept.getParent() != null && parent != null && !oldDept.getIdentity().equals(parent)) {
|
||||
getDept(deptId).removeMember(oldDept);
|
||||
getDept(parent).addChild(newDept);
|
||||
getDepartment(deptId).removeMember(oldDept);
|
||||
getDepartment(parent).addChild(newDept);
|
||||
}
|
||||
|
||||
} else if (parent != null) {
|
||||
getDept(parent).addChild(newDept);
|
||||
getDepartment(parent).addChild(newDept);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,9 +414,9 @@ public class UserStore {
|
|||
|
||||
// 组织关系
|
||||
for (Map.Entry<ID, Set<ID>> e : parentTemp.entrySet()) {
|
||||
BusinessUnit parent = getDept(e.getKey());
|
||||
BusinessUnit parent = getDepartment(e.getKey());
|
||||
for (ID child : e.getValue()) {
|
||||
parent.addChild(getDept(child));
|
||||
parent.addChild(getDepartment(child));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,6 +438,12 @@ public class UserStore {
|
|||
* @param role
|
||||
*/
|
||||
private void store(Role role) {
|
||||
Role old = ROLEs.get(role.getIdentity());
|
||||
if (old != null) {
|
||||
for (Principal user : old.getMembers()) {
|
||||
role.addMember(user);
|
||||
}
|
||||
}
|
||||
ROLEs.put((ID) role.getIdentity(), role);
|
||||
}
|
||||
|
||||
|
@ -421,6 +451,12 @@ public class UserStore {
|
|||
* @param dept
|
||||
*/
|
||||
private void store(Department dept) {
|
||||
Department old = DEPTs.get(dept.getIdentity());
|
||||
if (old != null) {
|
||||
for (Principal user : old.getMembers()) {
|
||||
dept.addMember(user);
|
||||
}
|
||||
}
|
||||
DEPTs.put((ID) dept.getIdentity(), dept);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ public class EasyMeta implements BaseMeta {
|
|||
BUILTIN_ENTITY.add("PickList");
|
||||
BUILTIN_ENTITY.add("LayoutConfig");
|
||||
BUILTIN_ENTITY.add("FilterConfig");
|
||||
BUILTIN_ENTITY.add("ShareAccess");
|
||||
|
||||
BUILTIN_FIELD.add(EntityHelper.createdOn);
|
||||
BUILTIN_FIELD.add(EntityHelper.createdBy);
|
||||
|
|
|
@ -18,7 +18,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.helper;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import java.util.Date;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
|
||||
/**
|
||||
* 前台提交的耗时操作
|
||||
|
@ -28,47 +30,43 @@ import com.alibaba.fastjson.JSON;
|
|||
*/
|
||||
public abstract class BulkTask implements Runnable {
|
||||
|
||||
private int totalQuantity = -1;
|
||||
private int completeQuantity = 0;
|
||||
private int total = -1;
|
||||
private int complete = 0;
|
||||
|
||||
private Date beginTime;
|
||||
|
||||
/**
|
||||
* 任务执行相关数据
|
||||
*/
|
||||
protected JSON data;
|
||||
|
||||
/**
|
||||
* @param data
|
||||
*/
|
||||
protected BulkTask(JSON data) {
|
||||
this.data = data;
|
||||
protected BulkTask() {
|
||||
this.beginTime = CalendarUtils.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalQuantity
|
||||
*/
|
||||
protected void setTotalQuantity(int totalQuantity) {
|
||||
this.totalQuantity = totalQuantity;
|
||||
protected void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param completeQuantity
|
||||
*/
|
||||
protected void setCompleteQuantity(int completeQuantity) {
|
||||
this.completeQuantity = completeQuantity;
|
||||
protected void setComplete(int complete) {
|
||||
this.complete = complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public int getTotalQuantity() {
|
||||
return totalQuantity;
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public int getCompleteQuantity() {
|
||||
return completeQuantity;
|
||||
public int getComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,13 +75,13 @@ public abstract class BulkTask implements Runnable {
|
|||
* @return
|
||||
*/
|
||||
public double getCompletePercent() {
|
||||
if (totalQuantity == -1 || completeQuantity == 0) {
|
||||
if (total == -1 || complete == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (totalQuantity == completeQuantity) {
|
||||
if (total == complete) {
|
||||
return 1;
|
||||
}
|
||||
return completeQuantity / totalQuantity;
|
||||
return complete / total;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,6 +90,15 @@ public abstract class BulkTask implements Runnable {
|
|||
* @return
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return totalQuantity == -1 || totalQuantity == completeQuantity;
|
||||
return total == -1 || total == complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务已耗时(ms)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getElapsedTime() {
|
||||
return CalendarUtils.now().getTime() - beginTime.getTime();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import cn.devezhao.commons.CodecUtils;
|
|||
import cn.devezhao.commons.ThreadPool;
|
||||
|
||||
/**
|
||||
* 大任务执行调度
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.helper;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
*/
|
||||
public abstract class TimeBulkTask extends BulkTask {
|
||||
|
||||
private Date beginTime;
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* @param data
|
||||
*/
|
||||
protected TimeBulkTask(JSON data) {
|
||||
super(data);
|
||||
this.beginTime = CalendarUtils.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Date getBeginTime() {
|
||||
return beginTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Date getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param endTime
|
||||
*/
|
||||
protected void setEndTime(Date endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务耗时(ms)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getElapsedTime() {
|
||||
Date end = getEndTime();
|
||||
end = end == null ? CalendarUtils.now() : end;
|
||||
return end.getTime() - beginTime.getTime();
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ public abstract class CacheTemplate<V> {
|
|||
}
|
||||
|
||||
protected Cache getCache() {
|
||||
return cacheManager.getCache("default");
|
||||
return cacheManager.getCache("rebuild");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,14 +18,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.helper.manager;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.server.Application;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
|
@ -58,7 +63,30 @@ public class NavManager extends LayoutManager {
|
|||
public static JSONArray getNavForPortal(HttpServletRequest request) {
|
||||
ID user = AppUtils.getRequestUser(request);
|
||||
Object[] cfgs = getLayoutConfigRaw("N", TYPE_NAVI, user);
|
||||
return cfgs == null ? JSON.parseArray("[]") : (JSONArray) cfgs[1];
|
||||
if (cfgs == null) {
|
||||
return JSON.parseArray("[]");
|
||||
}
|
||||
|
||||
// 过滤
|
||||
JSONArray navs = (JSONArray) cfgs[1];
|
||||
for (Iterator<Object> iter = navs.iterator(); iter.hasNext(); ) {
|
||||
JSONObject nav = (JSONObject) iter.next();
|
||||
if ("ENTITY".equalsIgnoreCase(nav.getString("type"))) {
|
||||
String entity = nav.getString("value");
|
||||
if (!MetadataHelper.containsEntity(entity)) {
|
||||
LOG.warn("Unknow entity in nav : " + entity);
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||
if (!Application.getSecurityManager().allowedR(user, entityMeta.getEntityCode())) {
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return navs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,7 +124,7 @@ public class EntityHelper {
|
|||
public static final String owningUser = "owningUser";
|
||||
public static final String owningDept = "owningDept";
|
||||
|
||||
// 实体代码
|
||||
// 系统实体
|
||||
|
||||
public static final int User = 1;
|
||||
public static final int Department = 2;
|
||||
|
@ -138,4 +138,6 @@ public class EntityHelper {
|
|||
public static final int LayoutConfig = 13;
|
||||
public static final int FilterConfig = 14;
|
||||
|
||||
public static final int ShareAccess = 20;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,10 +18,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.metadata.MetadataException;
|
||||
|
||||
/**
|
||||
|
@ -106,4 +110,26 @@ public class MetadataHelper {
|
|||
Entity entity = getEntity(entityName);
|
||||
return entity.getField(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* <tt>reference</tt> 中的哪些字段引用了 <tt>source</tt>
|
||||
*
|
||||
* @param source
|
||||
* @param reference
|
||||
* @return
|
||||
*/
|
||||
public static Field[] getReferenceToFields(Entity source, Entity reference) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : reference.getFields()) {
|
||||
if (field.getType() != FieldType.REFERENCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity ref = field.getReferenceEntities()[0];
|
||||
if (ref.getEntityCode() == source.getEntityCode()) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
return fields.toArray(new Field[fields.size()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@ import java.util.List;
|
|||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.server.entityhub.DisplayType;
|
||||
import com.rebuild.server.entityhub.EasyMeta;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
|
@ -44,6 +46,16 @@ public class AdvFilterParser {
|
|||
private Entity rootEntity;
|
||||
private JSONObject filterExp;
|
||||
|
||||
/**
|
||||
* @param filterExp
|
||||
*/
|
||||
public AdvFilterParser(JSONObject filterExp) {
|
||||
String entity = filterExp.getString("entity");
|
||||
Assert.notNull(entity, "[entity] node can't be blank");
|
||||
this.rootEntity = MetadataHelper.getEntity(entity);
|
||||
this.filterExp = filterExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rootEntity
|
||||
* @param filterExp
|
||||
|
|
|
@ -32,9 +32,9 @@ public abstract class BaseService {
|
|||
|
||||
final protected PersistManagerFactory aPMFactory;
|
||||
|
||||
protected BaseService(PersistManagerFactory factory) {
|
||||
protected BaseService(PersistManagerFactory aPMFactory) {
|
||||
super();
|
||||
this.aPMFactory = factory;
|
||||
this.aPMFactory = aPMFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
*/
|
||||
public class BulkAssign extends BulkOperator {
|
||||
|
||||
public BulkAssign(BulkContext context, GeneralEntityService ges) {
|
||||
super(context, ges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec() {
|
||||
ID[] records = getWillRecords();
|
||||
|
||||
int complated = 0;
|
||||
int assigned = 0;
|
||||
|
||||
for (ID id : records) {
|
||||
if (Application.getSecurityManager().allowedA(context.getOpUser(), id)) {
|
||||
int a = ges.assign(id, context.getToUser(), context.getCascades());
|
||||
assigned += (a > 0 ? 1 : 0);
|
||||
} else {
|
||||
LOG.warn("No have privileges to ASSIGN : " + context.getOpUser() + " > " + id);
|
||||
}
|
||||
|
||||
complated++;
|
||||
setComplete(complated);
|
||||
}
|
||||
return assigned;
|
||||
}
|
||||
}
|
121
src/main/java/com/rebuild/server/service/base/BulkContext.java
Normal file
121
src/main/java/com/rebuild/server/service/base/BulkContext.java
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
|
||||
import cn.devezhao.bizz.privileges.Permission;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
* @author devezhao
|
||||
* @since 10/17/2018
|
||||
*/
|
||||
public class BulkContext {
|
||||
|
||||
// 操作用户
|
||||
private ID opUser;
|
||||
// 执行动作
|
||||
private Permission action;
|
||||
// [目标用户]
|
||||
private ID toUser;
|
||||
|
||||
// 待操作记录
|
||||
private ID[] records;
|
||||
// 待操作过滤条件(通过条件确定记录)
|
||||
private JSONObject filterExp;
|
||||
|
||||
// 级联操作实体
|
||||
private String cascades[];
|
||||
|
||||
private Entity mainEntity;
|
||||
|
||||
/**
|
||||
* @param opUser
|
||||
* @param action
|
||||
* @param toUser
|
||||
* @param cascades
|
||||
* @param records
|
||||
*/
|
||||
public BulkContext(ID opUser, Permission action, ID toUser, String cascades[], ID[] records) {
|
||||
Assert.isTrue(records.length <= 200, "最多允许操作200条记录");
|
||||
this.opUser = opUser;
|
||||
this.action = action;
|
||||
this.toUser = toUser;
|
||||
this.cascades = cascades;
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param opUser
|
||||
* @param action
|
||||
* @param toUser
|
||||
* @param cascades
|
||||
* @param filterExp
|
||||
*/
|
||||
public BulkContext(ID opUser, Permission action, ID toUser, String cascades[], JSONObject filterExp) {
|
||||
this.opUser = opUser;
|
||||
this.action = action;
|
||||
this.toUser = toUser;
|
||||
this.cascades = cascades;
|
||||
this.filterExp = filterExp;
|
||||
}
|
||||
|
||||
public ID getOpUser() {
|
||||
return opUser;
|
||||
}
|
||||
|
||||
public Permission getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public ID getToUser() {
|
||||
return toUser;
|
||||
}
|
||||
|
||||
public String[] getCascades() {
|
||||
return cascades == null ? ArrayUtils.EMPTY_STRING_ARRAY : cascades;
|
||||
}
|
||||
|
||||
public ID[] getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
public JSONObject getFilterExp() {
|
||||
return filterExp;
|
||||
}
|
||||
|
||||
public Entity getMainEntity() {
|
||||
if (mainEntity != null) {
|
||||
return mainEntity;
|
||||
}
|
||||
|
||||
if (records != null) {
|
||||
mainEntity = MetadataHelper.getEntity(records[0].getEntityCode());
|
||||
} else {
|
||||
mainEntity = MetadataHelper.getEntity(filterExp.getString("entity"));
|
||||
}
|
||||
return mainEntity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 10/16/2018
|
||||
*/
|
||||
public class BulkDelete extends BulkOperator {
|
||||
|
||||
public BulkDelete(BulkContext context, GeneralEntityService ges) {
|
||||
super(context, ges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec() {
|
||||
ID[] records = getWillRecords();
|
||||
|
||||
int complated = 0;
|
||||
int deleted = 0;
|
||||
|
||||
for (ID id : records) {
|
||||
if (Application.getSecurityManager().allowedD(context.getOpUser(), id)) {
|
||||
int a = ges.delete(id);
|
||||
deleted += (a > 0 ? 1 : 0);
|
||||
} else {
|
||||
LOG.warn("No have privileges to DELETE : " + context.getOpUser() + " > " + id);
|
||||
}
|
||||
|
||||
complated++;
|
||||
setComplete(complated);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.server.helper.BulkTask;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.server.query.AdvFilterParser;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
* 批量操作
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 10/16/2018
|
||||
*/
|
||||
public abstract class BulkOperator extends BulkTask {
|
||||
|
||||
protected static final Log LOG = LogFactory.getLog(BulkOperator.class);
|
||||
|
||||
final protected BulkContext context;
|
||||
final protected GeneralEntityService ges;
|
||||
|
||||
private ID[] records;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param ges
|
||||
*/
|
||||
protected BulkOperator(BulkContext context, GeneralEntityService ges) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.ges = ges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待操作记录
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected ID[] getWillRecords() {
|
||||
if (this.records != null) {
|
||||
return this.records;
|
||||
}
|
||||
|
||||
if (context.getRecords() != null) {
|
||||
this.records = context.getRecords();
|
||||
setTotal(this.records.length);
|
||||
return this.records;
|
||||
}
|
||||
|
||||
JSONObject filterExp = context.getFilterExp();
|
||||
|
||||
AdvFilterParser filterParser = new AdvFilterParser(filterExp);
|
||||
String sqlWhere = filterParser.toSqlWhere();
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(filterExp.getString("entity"));
|
||||
String sql = "select %s from %s where (1=1) and " + sqlWhere;
|
||||
sql = String.format(sql, entity.getPrimaryField().getName(), entity.getName());
|
||||
|
||||
// TODO 解析过滤并查询结果
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际执行
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
abstract public Object exec();
|
||||
|
||||
}
|
|
@ -18,21 +18,39 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.server.helper.TimeBulkTask;
|
||||
import com.rebuild.server.Application;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
*/
|
||||
public class BatchModifiedTask extends TimeBulkTask {
|
||||
public class BulkShare extends BulkOperator {
|
||||
|
||||
protected BatchModifiedTask(JSON data) {
|
||||
super(data);
|
||||
public BulkShare(BulkContext context, GeneralEntityService ges) {
|
||||
super(context, ges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
public Object exec() {
|
||||
ID[] records = getWillRecords();
|
||||
|
||||
int complated = 0;
|
||||
int shared = 0;
|
||||
|
||||
for (ID id : records) {
|
||||
if (Application.getSecurityManager().allowedS(context.getOpUser(), id)) {
|
||||
int a = ges.share(id, context.getToUser(), context.getCascades());
|
||||
shared += (a > 0 ? 1 : 0);
|
||||
} else {
|
||||
LOG.warn("No have privileges to SHARE : " + context.getOpUser() + " > " + id);
|
||||
}
|
||||
|
||||
complated++;
|
||||
setComplete(complated);
|
||||
}
|
||||
return shared;
|
||||
}
|
||||
}
|
|
@ -18,18 +18,35 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
import com.rebuild.server.bizz.privileges.PrivilegesGuardInterceptor;
|
||||
import com.rebuild.server.bizz.privileges.User;
|
||||
import com.rebuild.server.helper.BulkTaskExecutor;
|
||||
import com.rebuild.server.metadata.EntityHelper;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.server.service.BaseService;
|
||||
|
||||
import cn.devezhao.bizz.privileges.Permission;
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.PersistManagerFactory;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
* 业务实体用,会带有系统设置规则的执行
|
||||
* 业务实体用
|
||||
* - 会带有系统设置规则的执行,详见 {@link PrivilegesGuardInterceptor}
|
||||
* - 会开启一个事务,详见 <tt>application-ctx.xml</tt> 配置
|
||||
*
|
||||
* @author zhaofang123@gmail.com
|
||||
* @since 11/06/2017
|
||||
|
@ -51,48 +68,193 @@ public class GeneralEntityService extends BaseService {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取级联操作记录
|
||||
*
|
||||
* @param main 主记录
|
||||
* @param cascades 级联实体
|
||||
* @param action 动作
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Set<ID>> getCascadeRecords(ID main, String[] cascades, Permission action) {
|
||||
if (cascades == null || cascades.length == 0) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, Set<ID>> cascadeRecordsMap = new HashMap<>();
|
||||
Entity mainEntity = MetadataHelper.getEntity(main.getEntityCode());
|
||||
for (String ref : cascades) {
|
||||
Entity refEntity = MetadataHelper.getEntity(ref);
|
||||
|
||||
String sql = "select %s from %s where ( ";
|
||||
sql = String.format(sql, refEntity.getPrimaryField().getName(), refEntity.getName());
|
||||
|
||||
Field[] refToFields = MetadataHelper.getReferenceToFields(mainEntity, refEntity);
|
||||
for (Field refToField : refToFields) {
|
||||
sql += refToField.getName() + " = '" + main + "' or ";
|
||||
}
|
||||
sql = sql.substring(0, sql.length() - 4); // remove last ' or '
|
||||
sql += " )";
|
||||
|
||||
// TODO 此处查询权限不对,应该查询能分派的记录,而非可读的记录
|
||||
|
||||
Object[][] array = Application.createQuery(sql).array();
|
||||
Set<ID> records = new HashSet<>();
|
||||
for (Object[] o : array) {
|
||||
records.add((ID) o[0]);
|
||||
}
|
||||
cascadeRecordsMap.put(ref, records);
|
||||
}
|
||||
return cascadeRecordsMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(ID record) {
|
||||
return super.delete(record);
|
||||
return delete(record, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param records
|
||||
* @return 实际删除数量。请注意请求删除数量和实际删除数量可能不一致,因为可能没有删除权限
|
||||
* 删除,带级联删除选项
|
||||
*
|
||||
* @see #delete(ID)
|
||||
* @param record
|
||||
* @param cascades 级联删除的实体
|
||||
* @return
|
||||
*/
|
||||
public int bulkDelete(ID records[]) {
|
||||
ID user = Application.currentCallerUser();
|
||||
int deleted = 0;
|
||||
for (ID id : records) {
|
||||
if (Application.getSecurityManager().allowed(user, id, BizzPermission.DELETE)) {
|
||||
int affected = this.delete(id);
|
||||
deleted += (affected > 0 ? 1 : 0);
|
||||
} else {
|
||||
LOG.warn("No have privileges to delete : " + user + " > " + id);
|
||||
public int delete(ID record, String[] cascades) {
|
||||
super.delete(record);
|
||||
|
||||
int affected = 1;
|
||||
|
||||
Map<String, Set<ID>> cass = getCascadeRecords(record, cascades, BizzPermission.DELETE);
|
||||
for (Map.Entry<String, Set<ID>> e : cass.entrySet()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("级联删除 - " + e.getKey() + "/" + e.getValue().size());
|
||||
}
|
||||
for (ID id : e.getValue()) {
|
||||
affected += delete(id, null);
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
return affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分派
|
||||
*
|
||||
* @param record
|
||||
* @param to
|
||||
* @param cascades 级联分派的实体
|
||||
* @return
|
||||
*/
|
||||
public int assign(ID record, ID to, String[] cascades) {
|
||||
ID currentOwn = Application.getRecordOwningCache().getOwningUser(record);
|
||||
if (currentOwn.equals(to)) {
|
||||
LOG.warn("记录所属人未变化,忽略本次操作 : " + record);
|
||||
return 1;
|
||||
}
|
||||
|
||||
User toUser = Application.getUserStore().getUser(to);
|
||||
|
||||
Record assign = EntityHelper.forUpdate(record, (ID) toUser.getIdentity());
|
||||
assign.setID(EntityHelper.owningUser, (ID) toUser.getIdentity());
|
||||
assign.setID(EntityHelper.owningDept, (ID) toUser.getOwningDept().getIdentity());
|
||||
super.update(assign);
|
||||
|
||||
int affected = 1;
|
||||
|
||||
Map<String, Set<ID>> cass = getCascadeRecords(record, cascades, BizzPermission.ASSIGN);
|
||||
for (Map.Entry<String, Set<ID>> e : cass.entrySet()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("级联分派 - " + e.getKey() + "/" + e.getValue().size());
|
||||
}
|
||||
|
||||
for (ID id : e.getValue()) {
|
||||
affected += assign(id, to, null);
|
||||
}
|
||||
}
|
||||
return affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 共享
|
||||
*
|
||||
* @param recordId
|
||||
* @param record
|
||||
* @param to
|
||||
* @param cascades 级联共享的实体
|
||||
* @return
|
||||
*/
|
||||
public int share(ID recordId) {
|
||||
return 0;
|
||||
public int share(ID record, ID to, String[] cascades) {
|
||||
Object[] exists = aPMFactory.createQuery(
|
||||
"select accessId,rights from ShareAccess where recordId = ? and shareTo = ?")
|
||||
.setParameter(1, record.toLiteral())
|
||||
.setParameter(2, to.toLiteral())
|
||||
.unique();
|
||||
if (exists != null) {
|
||||
LOG.warn("记录已分享过,忽略本次操作 : " + record);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ID currentUser = Application.currentCallerUser();
|
||||
|
||||
Record share = EntityHelper.forNew(EntityHelper.ShareAccess, currentUser);
|
||||
share.setInt("entity", record.getEntityCode());
|
||||
share.setString("recordId", record.toLiteral());
|
||||
share.setString("shareTo", to.toLiteral());
|
||||
share.setInt("rights", 999);
|
||||
super.create(share);
|
||||
|
||||
int affected = 1;
|
||||
|
||||
Map<String, Set<ID>> cass = getCascadeRecords(record, cascades, BizzPermission.SHARE);
|
||||
for (Map.Entry<String, Set<ID>> e : cass.entrySet()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("级联共享 - " + e.getKey() + "/" + e.getValue().size());
|
||||
}
|
||||
|
||||
for (ID id : e.getValue()) {
|
||||
affected += share(id, to, null);
|
||||
}
|
||||
}
|
||||
return affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配
|
||||
* 批量操作
|
||||
*
|
||||
* @param recordId
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public int assgin(ID recordId) {
|
||||
return 0;
|
||||
public int bulk(BulkContext context) {
|
||||
BulkOperator operator = buildBulkOperator(context);
|
||||
Object affected = operator.exec();
|
||||
return (Integer) affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作-异步
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*
|
||||
* @see BulkTaskExecutor
|
||||
*/
|
||||
public String bulkAsync(BulkContext context) {
|
||||
BulkOperator operator = buildBulkOperator(context);
|
||||
String taskid = BulkTaskExecutor.submit(operator);
|
||||
return taskid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private BulkOperator buildBulkOperator(BulkContext context) {
|
||||
if (context.getAction() == BizzPermission.DELETE) {
|
||||
return new BulkDelete(context, this);
|
||||
} else if (context.getAction() == BizzPermission.ASSIGN) {
|
||||
return new BulkAssign(context, this);
|
||||
} else if (context.getAction() == BizzPermission.SHARE) {
|
||||
return new BulkShare(context, this);
|
||||
}
|
||||
throw new UnsupportedOperationException("Unsupported bulk action : " + context.getAction());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,13 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.server.helper.TimeBulkTask;
|
||||
package com.rebuild.server.service.notification;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
* @since 10/17/2018
|
||||
*/
|
||||
public class BatchShareTask extends TimeBulkTask {
|
||||
public class Message {
|
||||
|
||||
protected BatchShareTask(JSON data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
}
|
||||
}
|
|
@ -16,23 +16,24 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.server.service.base;
|
||||
package com.rebuild.server.service.notification;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.server.helper.TimeBulkTask;
|
||||
import com.rebuild.server.service.BaseService;
|
||||
|
||||
import cn.devezhao.persist4j.PersistManagerFactory;
|
||||
|
||||
/**
|
||||
* 通知/消息 服务
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 09/29/2018
|
||||
* @since 10/17/2018
|
||||
*/
|
||||
public class BatchAssignTask extends TimeBulkTask {
|
||||
public class NotificationService extends BaseService {
|
||||
|
||||
protected BatchAssignTask(JSON data) {
|
||||
super(data);
|
||||
protected NotificationService(PersistManagerFactory aPMFactory) {
|
||||
super(aPMFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
public void send(Message message) {
|
||||
}
|
||||
}
|
|
@ -73,7 +73,7 @@ public class UserControll extends BaseControll {
|
|||
|
||||
JSONArray firsts = new JSONArray();
|
||||
for (Object dept[] : topDepts) {
|
||||
Department first = Application.getUserStore().getDept((ID) dept[0]);
|
||||
Department first = Application.getUserStore().getDepartment((ID) dept[0]);
|
||||
firsts.add(recursiveDeptTree(first));
|
||||
}
|
||||
writeSuccess(response, firsts);
|
||||
|
|
|
@ -21,8 +21,10 @@ package com.rebuild.web.base;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -75,4 +77,25 @@ public class MetadataGet extends BaseControll {
|
|||
}
|
||||
writeSuccess(response, list);
|
||||
}
|
||||
|
||||
@RequestMapping("references")
|
||||
public void ref(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||
|
||||
Set<Entity> references = new HashSet<>();
|
||||
|
||||
Field[] rtFields = entityMeta.getReferenceToFields();
|
||||
for (Field field : rtFields) {
|
||||
Entity own = field.getOwnEntity();
|
||||
references.add(own);
|
||||
}
|
||||
|
||||
List<String[]> list = new ArrayList<>();
|
||||
for (Entity e : references) {
|
||||
EasyMeta easy = new EasyMeta(e);
|
||||
list.add(new String[] { easy.getName(), easy.getLabel() });
|
||||
}
|
||||
writeSuccess(response, list);
|
||||
}
|
||||
}
|
||||
|
|
91
src/main/java/com/rebuild/web/base/SimpleSearch.java
Normal file
91
src/main/java/com/rebuild/web/base/SimpleSearch.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
rebuild - Building your system freely.
|
||||
Copyright (C) 2018 devezhao <zhaofang123@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.rebuild.web.base;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.rebuild.server.Application;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.web.BaseControll;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
|
||||
/**
|
||||
* 公用搜索
|
||||
*
|
||||
* @author zhaofang123@gmail.com
|
||||
* @since 08/24/2018
|
||||
*/
|
||||
@Controller
|
||||
public class SimpleSearch extends BaseControll {
|
||||
|
||||
@RequestMapping("/commons/search")
|
||||
public void search(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String qfields = getParameterNotNull(request, "qfields");
|
||||
String q = getParameter(request, "q");
|
||||
|
||||
if (StringUtils.isBlank(q)) {
|
||||
writeSuccess(response, ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
return;
|
||||
}
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
|
||||
Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||
Field idFiled = entityMeta.getPrimaryField();
|
||||
Field nameField = entityMeta.getNameField();
|
||||
|
||||
String sql = "select %s,%s from %s";
|
||||
sql = String.format(sql, idFiled.getName(), nameField.getName(), entityMeta.getName());
|
||||
|
||||
List<String> or = new ArrayList<>();
|
||||
for (String qField : qfields.split(",")) {
|
||||
if (!entityMeta.containsField(qField)) {
|
||||
LOG.warn("No field for search : " + qField);
|
||||
} else {
|
||||
or.add(qField + " like '%" + q + "%'");
|
||||
}
|
||||
}
|
||||
sql += " where ( " + StringUtils.join(or, " or ") + " ) order by modifiedOn desc";
|
||||
|
||||
Object[][] array = Application.createQuery(sql).setLimit(10).array();
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object[] o : array) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("id", o[0].toString());
|
||||
map.put("text", o[1]);
|
||||
result.add(map);
|
||||
}
|
||||
writeSuccess(response, result);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import java.util.Set;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -37,10 +38,13 @@ import com.alibaba.fastjson.JSONObject;
|
|||
import com.rebuild.server.Application;
|
||||
import com.rebuild.server.metadata.EntityHelper;
|
||||
import com.rebuild.server.metadata.MetadataHelper;
|
||||
import com.rebuild.server.service.base.BulkContext;
|
||||
import com.rebuild.server.service.base.GeneralEntityService;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.web.BaseControll;
|
||||
import com.rebuild.web.InvalidRequestException;
|
||||
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
|
@ -64,10 +68,11 @@ public class GeneralRecordControll extends BaseControll {
|
|||
JSON formJson = ServletUtils.getRequestJson(request);
|
||||
|
||||
Record record = EntityHelper.parse((JSONObject) formJson, user);
|
||||
if (record.getPrimary() == null
|
||||
&& !Application.getSecurityManager().allowedC(user, record.getEntity().getEntityCode())) {
|
||||
writeFailure(response, "没有新建权限");
|
||||
return;
|
||||
if (record.getPrimary() == null) {
|
||||
if (!Application.getSecurityManager().allowedC(user, record.getEntity().getEntityCode())) {
|
||||
writeFailure(response, "没有新建权限");
|
||||
return;
|
||||
}
|
||||
} else if (!Application.getSecurityManager().allowedU(user, record.getPrimary())) {
|
||||
writeFailure(response, "没有编辑权限");
|
||||
return;
|
||||
|
@ -82,48 +87,99 @@ public class GeneralRecordControll extends BaseControll {
|
|||
|
||||
@RequestMapping("record-delete")
|
||||
public void delete(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
ID user = getRequestUser(request);
|
||||
String ids = getParameterNotNull(request, "id");
|
||||
|
||||
// TODO 级联删除
|
||||
|
||||
Set<ID> idList = new HashSet<>();
|
||||
int deleteEntityCode = 0;
|
||||
for (String id : ids.split(",")) {
|
||||
ID id0 = ID.valueOf(id);
|
||||
if (deleteEntityCode == 0) {
|
||||
deleteEntityCode = id0.getEntityCode();
|
||||
}
|
||||
if (deleteEntityCode != id0.getEntityCode()) {
|
||||
writeFailure(response, "只能批量删除同一实体的记录");
|
||||
return;
|
||||
}
|
||||
|
||||
idList.add(ID.valueOf(id));
|
||||
}
|
||||
|
||||
if (idList.isEmpty()) {
|
||||
final ID user = getRequestUser(request);
|
||||
final ID[] ids = parseIdList(request);
|
||||
if (ids.length == 0) {
|
||||
writeFailure(response, "没有要删除的记录");
|
||||
return;
|
||||
}
|
||||
|
||||
ID firstId = idList.iterator().next();
|
||||
if (!Application.getSecurityManager().allowedD(user, firstId)) {
|
||||
Entity entity = MetadataHelper.getEntity(ids[0].getEntityCode());
|
||||
if (!Application.getSecurityManager().allowedD(user, entity.getEntityCode())) {
|
||||
writeFailure(response, "没有删除权限");
|
||||
return;
|
||||
}
|
||||
|
||||
GeneralEntityService ges = Application.getGeneralEntityService(deleteEntityCode);
|
||||
int deleted = 0;
|
||||
if (idList.size() == 1) {
|
||||
deleted = ges.delete(firstId);
|
||||
String[] cascades = parseCascades(request);
|
||||
GeneralEntityService ges = Application.getGeneralEntityService(entity.getEntityCode());
|
||||
|
||||
int affected = 0;
|
||||
if (ids.length == 1) {
|
||||
affected = ges.delete(ids[0], cascades);
|
||||
} else {
|
||||
deleted = ges.bulkDelete(idList.toArray(new ID[idList.size()]));
|
||||
BulkContext context = new BulkContext(user, BizzPermission.DELETE, null, cascades, ids);
|
||||
affected = ges.bulk(context);
|
||||
}
|
||||
|
||||
JSON ret = JSONUtils.toJSONObject(
|
||||
new String[] { "deleted", "request_delete" },
|
||||
new Object[] { deleted, idList.size() });
|
||||
new String[] { "deleted", "requests" },
|
||||
new Object[] { affected, ids.length });
|
||||
writeSuccess(response, ret);
|
||||
}
|
||||
|
||||
@RequestMapping("record-assign")
|
||||
public void assign(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
final ID user = getRequestUser(request);
|
||||
final ID[] ids = parseIdList(request);
|
||||
if (ids.length == 0) {
|
||||
writeFailure(response, "没有要分派的记录");
|
||||
return;
|
||||
}
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(ids[0].getEntityCode());
|
||||
if (!Application.getSecurityManager().allowedA(user, entity.getEntityCode())) {
|
||||
writeFailure(response, "没有分派权限");
|
||||
return;
|
||||
}
|
||||
|
||||
ID assignTo = getIdParameterNotNull(request, "to");
|
||||
String[] cascades = parseCascades(request);
|
||||
GeneralEntityService ges = Application.getGeneralEntityService(entity.getEntityCode());
|
||||
|
||||
int affected = 0;
|
||||
if (ids.length == 1) {
|
||||
affected = ges.assign(ids[0], assignTo, cascades);
|
||||
} else {
|
||||
BulkContext context = new BulkContext(user, BizzPermission.ASSIGN, assignTo, cascades, ids);
|
||||
affected = ges.bulk(context);
|
||||
}
|
||||
|
||||
JSON ret = JSONUtils.toJSONObject(
|
||||
new String[] { "shared", "requests" },
|
||||
new Object[] { affected, ids.length });
|
||||
writeSuccess(response, ret);
|
||||
}
|
||||
|
||||
@RequestMapping("record-share")
|
||||
public void share(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
final ID user = getRequestUser(request);
|
||||
final ID[] ids = parseIdList(request);
|
||||
if (ids.length == 0) {
|
||||
writeFailure(response, "没有要共享的记录");
|
||||
return;
|
||||
}
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(ids[0].getEntityCode());
|
||||
if (!Application.getSecurityManager().allowedA(user, entity.getEntityCode())) {
|
||||
writeFailure(response, "没有共享权限");
|
||||
return;
|
||||
}
|
||||
|
||||
ID shareTo = getIdParameterNotNull(request, "to");
|
||||
String[] cascades = parseCascades(request);
|
||||
GeneralEntityService ges = Application.getGeneralEntityService(entity.getEntityCode());
|
||||
|
||||
int affected = 0;
|
||||
if (ids.length == 1) {
|
||||
affected = ges.share(ids[0], shareTo, cascades);
|
||||
} else {
|
||||
BulkContext context = new BulkContext(user, BizzPermission.SHARE, shareTo, cascades, ids);
|
||||
affected = ges.bulk(context);
|
||||
}
|
||||
|
||||
JSON ret = JSONUtils.toJSONObject(
|
||||
new String[] { "shared", "requests" },
|
||||
new Object[] { affected, ids.length });
|
||||
writeSuccess(response, ret);
|
||||
}
|
||||
|
||||
|
@ -164,4 +220,45 @@ public class GeneralRecordControll extends BaseControll {
|
|||
map.put(entity.getPrimaryField().getName(), id);
|
||||
writeSuccess(response, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作ID列表
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private ID[] parseIdList(HttpServletRequest request) {
|
||||
String ids = getParameterNotNull(request, "id");
|
||||
Set<ID> idList = new HashSet<>();
|
||||
int sameEntityCode = 0;
|
||||
for (String id : ids.split(",")) {
|
||||
ID id0 = ID.valueOf(id);
|
||||
if (sameEntityCode == 0) {
|
||||
sameEntityCode = id0.getEntityCode();
|
||||
}
|
||||
if (sameEntityCode != id0.getEntityCode()) {
|
||||
throw new InvalidRequestException("只能批量删除同一实体的记录");
|
||||
}
|
||||
idList.add(ID.valueOf(id));
|
||||
}
|
||||
return idList.toArray(new ID[idList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 级联操作实体
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private String[] parseCascades(HttpServletRequest request) {
|
||||
String cascades = getParameter(request, "cascades");
|
||||
if (StringUtils.isBlank(cascades)) {
|
||||
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
String[] cass = cascades.split(",");
|
||||
|
||||
// TODO 验证实体???
|
||||
|
||||
return cass;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,11 +48,10 @@ import cn.devezhao.persist4j.dialect.FieldType;
|
|||
* @since 08/24/2018
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/app/commons/")
|
||||
public class ReferenceSearch extends BaseControll {
|
||||
|
||||
@RequestMapping("search")
|
||||
public void search(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
@RequestMapping("/app/entity/ref-search")
|
||||
public void searchRefs(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String field = getParameterNotNull(request, "field");
|
||||
String q = getParameter(request, "q");
|
||||
|
@ -61,11 +60,12 @@ public class ReferenceSearch extends BaseControll {
|
|||
writeSuccess(response, ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
return;
|
||||
}
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
|
||||
Entity root = MetadataHelper.getEntity(entity);
|
||||
Field referenceField = root.getField(field);
|
||||
Entity referenceEntity = referenceField.getReferenceEntities()[0];
|
||||
Field nameField = referenceEntity.getNameField();
|
||||
Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||
Field refField = entityMeta.getField(field);
|
||||
Entity refEntity = refField.getReferenceEntities()[0];
|
||||
Field nameField = refEntity.getNameField();
|
||||
if (nameField == null) {
|
||||
writeSuccess(response, ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
return;
|
||||
|
@ -76,20 +76,18 @@ public class ReferenceSearch extends BaseControll {
|
|||
nameField2str = "&" + nameField2str;
|
||||
}
|
||||
|
||||
String searchSql = "select %s,%s from %s where %s ";
|
||||
searchSql = String.format(searchSql,
|
||||
referenceEntity.getPrimaryField().getName(), nameField2str, referenceEntity.getName(), nameField2str);
|
||||
searchSql += "like '%" + StringEscapeUtils.escapeSql(q) + "%'";
|
||||
String sql = "select %s,%s from %s where %s ";
|
||||
sql = String.format(sql, refEntity.getPrimaryField().getName(), nameField2str, refEntity.getName(), nameField2str);
|
||||
sql += "like '%" + q + "%' order by modifiedOn desc";
|
||||
|
||||
Object[][] array = Application.createQuery(searchSql).setLimit(10).array();
|
||||
|
||||
List<Object> list = new ArrayList<>();
|
||||
Object[][] array = Application.createQuery(sql).setLimit(10).array();
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object[] o : array) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("id", o[0].toString());
|
||||
map.put("text", o[1]);
|
||||
list.add(map);
|
||||
result.add(map);
|
||||
}
|
||||
writeSuccess(response, list);
|
||||
writeSuccess(response, result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,8 @@
|
|||
<tx:method name="update*" propagation="REQUIRED" />
|
||||
<tx:method name="assign*" propagation="REQUIRED" />
|
||||
<tx:method name="share*" propagation="REQUIRED" />
|
||||
<tx:method name="bulk*" propagation="REQUIRED" />
|
||||
<tx:method name="bulk*" propagation="REQUIRED" />
|
||||
<tx:method name="tx*" propagation="REQUIRED" />
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
|
@ -106,16 +107,20 @@
|
|||
<aop:advisor pointcut-ref="servicesPoint" advice-ref="txAdvice" />
|
||||
<aop:advisor pointcut-ref="servicesPoint" advice-ref="privilegesGuard" />
|
||||
</aop:config>
|
||||
|
||||
|
||||
<bean class="com.rebuild.server.entityhub.PickListService">
|
||||
<constructor-arg index="0" ref="PersistManagerFactory" />
|
||||
</bean>
|
||||
|
||||
<bean class="com.rebuild.server.service.CommonService">
|
||||
<constructor-arg index="0" ref="PersistManagerFactory" />
|
||||
</bean>
|
||||
|
||||
<bean name="GeneralEntityService" class="com.rebuild.server.service.base.GeneralEntityService">
|
||||
<bean class="com.rebuild.server.service.notification.NotificationService">
|
||||
<constructor-arg index="0" ref="PersistManagerFactory" />
|
||||
</bean>
|
||||
|
||||
<bean class="com.rebuild.server.entityhub.PickListService">
|
||||
<bean name="GeneralEntityService" class="com.rebuild.server.service.base.GeneralEntityService">
|
||||
<constructor-arg index="0" ref="PersistManagerFactory" />
|
||||
</bean>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# $Id: default-bizz-cfg.properties 7 2013-12-24 15:22:33Z zhaoff@qidapp.com $
|
||||
field.own-user=ownUser
|
||||
field.own-biz-unit=ownDept
|
||||
field.own-user=owningUser
|
||||
field.own-biz-unit=owningDept
|
||||
value.root-user=001-0000000000000001
|
||||
value.root-role=003-0000000000000001
|
|
@ -1,14 +1,29 @@
|
|||
<ehcache>
|
||||
<diskStore path="java.io.tmpdir" />
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
|
||||
updateCheck="false">
|
||||
|
||||
<defaultCache
|
||||
<diskStore path="java.io.tmpdir" />
|
||||
|
||||
<defaultCache
|
||||
maxElementsInMemory="1000000"
|
||||
eternal="false"
|
||||
timeToIdleSeconds="604800"
|
||||
eternal="false"
|
||||
timeToIdleSeconds="604800"
|
||||
timeToLiveSeconds="2592000"
|
||||
overflowToDisk="true"
|
||||
overflowToDisk="true"
|
||||
maxElementsOnDisk="100000000"
|
||||
diskPersistent="false"
|
||||
diskPersistent="false"
|
||||
diskExpiryThreadIntervalSeconds="120"
|
||||
memoryStoreEvictionPolicy="LRU" />
|
||||
|
||||
<cache name="rebuild"
|
||||
maxElementsInMemory="1000000"
|
||||
eternal="false"
|
||||
timeToIdleSeconds="604800"
|
||||
timeToLiveSeconds="2592000"
|
||||
overflowToDisk="true"
|
||||
maxElementsOnDisk="100000000"
|
||||
diskPersistent="false"
|
||||
diskExpiryThreadIntervalSeconds="120"
|
||||
memoryStoreEvictionPolicy="LRU" />
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<entity name="RolePrivileges" type-code="004" parent="false">
|
||||
<field name="privilegesId" type="primary" />
|
||||
<field name="roleId" type="reference" ref-entity="Role" />
|
||||
<field name="roleId" type="reference" ref-entity="Role" cascade="delete" nullable="false" />
|
||||
<field name="entity" type="string" max-length="100" nullable="false" default-value="N"/>
|
||||
<field name="zeroKey" type="string" description="其他权限KEY"/>
|
||||
<field name="definition" type="string" max-length="100" description="权限定义"/>
|
||||
|
@ -115,4 +115,13 @@
|
|||
<field name="applyTo" type="string" default-value="SELF" description="共享给哪些人,可选值: ALL/SELF/$MemberID(U/D/R)" />
|
||||
</entity>
|
||||
|
||||
<entity name="ShareAccess" type-code="020" description="记录共享">
|
||||
<field name="accessId" type="primary" />
|
||||
<field name="entity" type="int" nullable="false" description="哪个实体"/>
|
||||
<field name="recordId" type="string" max-length="20" nullable="false" updatable="false" description="记录ID" />
|
||||
<field name="shareTo" type="string" max-length="20" nullable="false" updatable="false" description="共享给谁 (User/Dept/Role)" />
|
||||
<field name="rights" type="int" nullable="false" updatable="false" description="共享权限(C=1,R=2,U=4,D=8)" />
|
||||
<index field-list="entity,recordId,shareTo"/>
|
||||
</entity>
|
||||
|
||||
</metadata-config>
|
|
@ -35,11 +35,11 @@ final String showName = UserHelper.getShowName(currentUser);
|
|||
<ul class="nav navbar-nav float-right rb-icons-nav">
|
||||
<% if (currentUser.isAdmin()) { %>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" href="${baseUrl}/admin/systems" title="系统设置"><span class="icon zmdi zmdi-settings"></span></a>
|
||||
<a class="nav-link" href="${baseUrl}/admin/systems" title="系统配置"><span class="icon zmdi zmdi-settings"></span></a>
|
||||
</li>
|
||||
<%} %>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" href="${baseUrl}/app/messages" title="消息中心"><span class="icon zmdi zmdi-notifications"></span></a>
|
||||
<a class="nav-link" href="${baseUrl}/app/notifications" title="通知"><span class="icon zmdi zmdi-notifications"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -101,8 +101,8 @@
|
|||
<table class="table table-priv">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="25%">权限项目</th>
|
||||
<th class="text-center"><span data-action="Z">允许</span></th>
|
||||
<th width="25%">权限项</th>
|
||||
<th class="text-center"><a data-action="Z">允许</a></th>
|
||||
<th>前置条件</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
|
@ -203,6 +203,10 @@ $(document).ready(function(){
|
|||
$('#priv-zero tbody .priv').click(function(){
|
||||
clickPriv($(this), 'Z')
|
||||
})
|
||||
$('#priv-zero thead th>a').click(function(){
|
||||
let privAll = $('#priv-zero tbody .priv[data-action="Z"]')
|
||||
clickPriv(privAll, 'Z')
|
||||
})
|
||||
$('#priv-zero tbody .name>a').click(function(){
|
||||
let el = $(this).parent().next().find('i.priv')
|
||||
clickPriv(el, 'Z')
|
||||
|
@ -211,8 +215,8 @@ $(document).ready(function(){
|
|||
})
|
||||
const clickPriv = function(elements, action) {
|
||||
if (action == 'C' || action == 'Z') {
|
||||
elements.toggleClass('R0')
|
||||
elements.toggleClass('R4')
|
||||
if (elements.first().hasClass('R0')) elements.removeClass('R0').addClass('R4')
|
||||
else elements.removeClass('R4').addClass('R0')
|
||||
} else {
|
||||
let clz = 'R0'
|
||||
if (elements.hasClass('R0')) clz = 'R1'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<%@ include file="/_include/Head.jsp"%>
|
||||
<title>验证管理员</title>
|
||||
<title>验证管理员身份</title>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
|
|
|
@ -3736,8 +3736,9 @@ input[type=submit].btn-block {
|
|||
|
||||
.dropdown-item.disabled,
|
||||
.dropdown-item:disabled {
|
||||
color: #878787;
|
||||
background-color: transparent
|
||||
color: #878787 !important;
|
||||
background-color: transparent !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
|
|
72
src/main/webapp/assets/js/assign-share.js
Normal file
72
src/main/webapp/assets/js/assign-share.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
var opType = opType || ['assign', '分派']
|
||||
$(document).ready(function(){
|
||||
let selected = parent.RbListPage._RbList.getSelectedRows()
|
||||
const ids = selected.map((item) => { return item[0] })
|
||||
const entity = $urlp('entity')
|
||||
|
||||
$('#records').text('选中的记录 (' + ids.length + '条)')
|
||||
|
||||
$('#toUser').select2({
|
||||
language: 'zh-CN',
|
||||
placeholder: '选择用户',
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: rb.baseUrl + '/commons/search',
|
||||
delay: 300,
|
||||
data: function(params) {
|
||||
let query = {
|
||||
entity: 'User',
|
||||
qfields: 'loginName,fullName,email',
|
||||
q: params.term,
|
||||
}
|
||||
return query
|
||||
},
|
||||
processResults: function(data){
|
||||
let rs = data.data.map((item) => { return item })
|
||||
return { results: rs }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$('.J_click-cass a').click(function(){
|
||||
$('.J_click-cass').remove();
|
||||
$('.J_cass').removeClass('hide');
|
||||
parent.RbListPage._ModalShare.resize()
|
||||
|
||||
$.get(rb.baseUrl + '/commons/metadata/references?entity=' + entity, function(res){
|
||||
$(res.data).each(function(){
|
||||
$('<option value="' + this[0] + '">' + this[1] + '</option>').appendTo('#cascades')
|
||||
})
|
||||
$('#cascades').select2({
|
||||
language: 'zh-CN',
|
||||
placeholder: '选择关联实体 (可选)',
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
$('.J_submit').click(function() {
|
||||
let to = $('#toUser').val()
|
||||
if (!to || to.length < 1){ rb.notice('请选择' + opType[1] + '给谁'); return }
|
||||
let cascades = $('#cascades').val()
|
||||
|
||||
let url = rb.baseUrl + '/app/entity/record-' + opType[0] + '?id=' + ids.join(',') + '&cascades=' + cascades.join(',') + '&to=' + to.join(',')
|
||||
$.post(url, function(res) {
|
||||
if (res.error_code == 0){
|
||||
rb.notice('已成功' + opType[1] + ' ' + (res.data.assigned || res.data.shared) + ' 条记录', 'success')
|
||||
if (parent.RbListPage) {
|
||||
if (parent.RbListPage._RbList) parent.RbListPage._RbList.reload()
|
||||
$('.J_cancel').trigger('click')
|
||||
}
|
||||
} else {
|
||||
rb.notice(res.error_msg || (opType[1] + '失败,请稍后重试'), 'danger')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$('.J_cancel').click(function(){
|
||||
if (parent.RbListPage) {
|
||||
if (parent.RbListPage._ModalAssign) parent.RbListPage._ModalAssign.hide()
|
||||
if (parent.RbListPage._ModalShare) parent.RbListPage._ModalShare.hide()
|
||||
}
|
||||
})
|
||||
});
|
|
@ -169,8 +169,8 @@ class RbForm extends React.Component {
|
|||
}
|
||||
// 保存后调用
|
||||
static postAfter(data) {
|
||||
if (window.rbList) window.rbList.reload()
|
||||
else if (parent.rbList) parent.rbList.reload()
|
||||
if (window.RbListPage) window.RbListPage._RbList.reload()
|
||||
else if (parent.RbListPage) parent.RbListPage._RbList.reload()
|
||||
if (window.rbFromView) location.reload()
|
||||
}
|
||||
}
|
||||
|
@ -596,7 +596,7 @@ class RbFormReference extends RbFormElement {
|
|||
allowClear: true,
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: rb.baseUrl + '/app/commons/search',
|
||||
url: rb.baseUrl + '/app/entity/ref-search',
|
||||
delay: 300,
|
||||
data: function(params) {
|
||||
let query = {
|
||||
|
|
|
@ -355,11 +355,13 @@ const RbListPage = {
|
|||
})
|
||||
|
||||
$(this.refs['rbalter']).find('.btn').button('loading')
|
||||
let that = this
|
||||
let thatModal = this
|
||||
$.post(rb.baseUrl + '/app/entity/record-delete?id=' + ids.join(','), function(res){
|
||||
if (res.error_code == 0){
|
||||
rbList.reload()
|
||||
that.hide()
|
||||
that._RbList.reload()
|
||||
thatModal.hide()
|
||||
if (res.data.deleted == res.data.requests) rb.notice('删除成功', 'success')
|
||||
else rb.notice('已成功删除 ' + res.data.deleted + ' 条记录', 'success')
|
||||
} else {
|
||||
rb.notice(res.error_msg || '删除失败,请稍后重试', 'danger')
|
||||
}
|
||||
|
@ -384,12 +386,14 @@ const RbListPage = {
|
|||
})
|
||||
|
||||
$('.J_assign').click(function(){
|
||||
if (that._RbList.getSelectedRows().length < 1) { rb.notice('请选择要分派的记录'); return}
|
||||
if (that._ModalAssign) that._ModalAssign.show()
|
||||
else that._ModalAssign = rb.modal(`${rb.baseUrl}/page/general-entity/assign?entity=entity${entity[1]}`, '分配记录')
|
||||
else that._ModalAssign = rb.modal(`${rb.baseUrl}/page/general-entity/assign?entity=${entity[1]}`, '分派记录')
|
||||
})
|
||||
$('.J_share').click(function(){
|
||||
if (that._RbList.getSelectedRows().length < 1) { rb.notice('请选择要共享的记录'); return}
|
||||
if (that._ModalShare) that._ModalShare.show()
|
||||
else that._ModalShare = rb.modal(`${rb.baseUrl}/page/general-entity/share?entity=entity${entity[1]}`, '共享记录')
|
||||
else that._ModalShare = rb.modal(`${rb.baseUrl}/page/general-entity/share?entity=${entity[1]}`, '共享记录')
|
||||
})
|
||||
|
||||
$('.J_columns').click(function(){
|
||||
|
|
|
@ -1,12 +1,48 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Insert title here</title>
|
||||
<%@ include file="/_include/Head.jsp"%>
|
||||
<title>分派</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class="dialog">
|
||||
<div class="main-content">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">分派哪些记录</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<div class="form-control-plaintext" id="records">选中的记录</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">分派给谁</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<select class="form-control form-control-sm" id="toUser" multiple="multiple">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row J_click-cass">
|
||||
<div class="col-sm-7 offset-sm-3">
|
||||
<a href="javascript:;">同时分派关联记录</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row J_cass hide">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">同时分派关联记录</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<select class="form-control form-control-sm" id="cascades" multiple="multiple">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row footer">
|
||||
<div class="col-sm-7 offset-sm-3">
|
||||
<button class="btn btn-primary J_submit" type="button" data-loading-text="请稍后">确定</button>
|
||||
<a class="btn btn-link J_cancel">取消</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<%@ include file="/_include/Foot.jsp"%>
|
||||
<script src="${baseUrl}/assets/js/assign-share.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -43,10 +43,12 @@
|
|||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">更多 <i class="icon zmdi zmdi-more-vert"></i></button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item J_share"><i class="icon zmdi zmdi-slideshare"></i> 共享</a>
|
||||
<a class="dropdown-item J_assign"><i class="icon zmdi zmdi-mail-reply-all"></i> 分配</a>
|
||||
<a class="dropdown-item J_assign"><i class="icon zmdi zmdi-mail-reply-all"></i> 分派</a>
|
||||
<!--
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item J_exports"><i class="icon zmdi zmdi-download"></i> 导出</a>
|
||||
<a class="dropdown-item J_imports"><i class="icon zmdi zmdi-upload"></i> 导入</a>
|
||||
-->
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item J_columns"><i class="icon zmdi zmdi-sort-amount-asc"></i> 列显示</a>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,51 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Insert title here</title>
|
||||
<%@ include file="/_include/Head.jsp"%>
|
||||
<title>共享</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class="dialog">
|
||||
<div class="main-content">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">共享哪些记录</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<div class="form-control-plaintext" id="records">选中的记录</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">共享给谁</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<select class="form-control form-control-sm" id="toUser" multiple="multiple">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row J_click-cass">
|
||||
<div class="col-sm-7 offset-sm-3">
|
||||
<a href="javascript:;">同时共享关联记录</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row J_cass hide">
|
||||
<label class="col-sm-3 col-form-label text-sm-right">同时共享关联记录</label>
|
||||
<div class="col-sm-7 col-lg-4">
|
||||
<select class="form-control form-control-sm" id="cascades" multiple="multiple">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row footer">
|
||||
<div class="col-sm-7 offset-sm-3">
|
||||
<button class="btn btn-primary J_submit" type="button" data-loading-text="请稍后">确定</button>
|
||||
<a class="btn btn-link J_cancel">取消</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<%@ include file="/_include/Foot.jsp"%>
|
||||
<script type="text/javascript">
|
||||
opType = ['share', '共享']
|
||||
</script>
|
||||
<script src="${baseUrl}/assets/js/assign-share.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -55,10 +55,7 @@ $(document).ready(function() {
|
|||
|
||||
$('.J_login-btn').click(function() {
|
||||
let user = $val('#user'), passwd = $val('#passwd');
|
||||
if (!user || !passwd){
|
||||
rb.notice('请输入用户名和密码')
|
||||
return;
|
||||
}
|
||||
if (!user || !passwd) return;
|
||||
|
||||
let btn = $(this).button('loading');
|
||||
$.post(rb.baseUrl + '/user/user-login?user=' + $encode(user) + '&passwd=' + $encode(passwd), function(res) {
|
||||
|
|
|
@ -50,8 +50,9 @@ public class SchemaGen {
|
|||
// gen(EntityHelper.MetaField);
|
||||
// gen(EntityHelper.PickList);
|
||||
// gen(EntityHelper.RolePrivileges);
|
||||
gen(EntityHelper.LayoutConfig);
|
||||
gen(EntityHelper.FilterConfig);
|
||||
// gen(EntityHelper.LayoutConfig);
|
||||
// gen(EntityHelper.FilterConfig);
|
||||
gen(EntityHelper.ShareAccess);
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue