我正在用android构建可管理用户和班次的应用。可以为每个用户分配多个班次,每个班次可以有多个用户。每个班次都关联一个日期。
这是我在firebase中构建数据库的方式:
{
"shift-assign" : {
"key1" : {
"111" : true,
"222" : true,
"888" : true
},
"key2" : {
"111" : true,
"222" : true
}
},
"shifts" : {
"key1" : {
"date" : "20191202",
"endTime" : "02:00",
"numOfEmps" : 4,
"startTime" : "21:00",
"wage" : 30
},
"key2" : {
"date" : "20191202",
"endTime" : "12:00",
"numOfEmps" : 3,
"startTime" : "08:00",
"wage" : 10
},
"key3" : {
"date" : "20191203",
"endTime" : "06:00",
"numOfEmps" : 3,
"startTime" : "00:00",
"wage" : 30
}
},
"user-assign" : {
"111" : {
"key1" : true,
"key2" : true
},
"222" : {
"key1" : true,
"key2" : true
},
"888" : {
"key1" : true
}
},
"users" : {
"111" : {
"active" : true,
"email" : "111",
"firstName" : "aaa",
"id" : "111",
"lastName" : "aaa",
"password" : "111",
"phone" : "1111111"
},
"222" : {
"active" : true,
"email" : "222",
"firstName" : "bbb",
"id" : "222",
"lastName" : "bbb",
"password" : "222",
"phone" : "4444444"
},
"888" : {
"active" : false,
"email" : "888",
"firstName" : "ccc",
"id" : "888",
"lastName" : "ccc",
"password" : "888",
"phone" : "5555555"
},
"99999" : {
"active" : true,
"firstName" : "Admin",
"id" : "99999",
"password" : "123"
}
}
}
当用户尝试将自己分配给班次时,我想先检查一些约束,然后再将其实际分配给班次。
我尝试使用事务,但是代码混乱了,因为我需要引用事务中的不同节点。
我的代码现在看起来像这样:
private void AssignUserToShift() {
// Constraint No. 0
// user cannot be assigned to shifts whose date < today
if(dates[0].compareTo(LocalDateTime.now().toLocalDate().format(dtf)) < 0){
Toast.makeText(getApplicationContext(), "Cannot assign to prior dates", Toast.LENGTH_SHORT).show();
return;
}
Map<String, Object> pathsToUpdate = new HashMap<>();
pathsToUpdate.put("shift-assign/" + shiftKey + "/" + userId, true);
pathsToUpdate.put("user-assign/" + userId + "/" + shiftKey, true);
mDatabase.updateChildren(pathsToUpdate, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(@Nullable DatabaseError databaseError, @NonNull DatabaseReference databaseReference) {
if(databaseError == null) {
// add to adapter's list in GUI
AddUserToList();
}else{
Toast.makeText(getApplicationContext(), "Could not update database entries", Toast.LENGTH_SHORT).show();
}
}
});
}
相反的方法:
private void RemoveUserFromShift() {
mUser user = userList.stream()
.filter(u -> u.getId().equals(userId))
.findAny()
.orElse(null);
if(user == null){
Toast.makeText(UsersAssignedActivity.this, "You are not assigned to this shift.", Toast.LENGTH_SHORT).show();
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(UsersAssignedActivity.this, R.style.AlertDialogCustom);
builder.setMessage("Do you wish to remove yourself from this shift?");
builder.setCancelable(false);
builder.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int i) {
Map<String, Object> pathsToDelete = new HashMap<>();
pathsToDelete.put("shift-assign/" + shiftKey + "/" + userId, null);
pathsToDelete.put("user-assign/" + userId + "/" + shiftKey, null);
mDatabase.updateChildren(pathsToDelete, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(@Nullable DatabaseError databaseError, @NonNull DatabaseReference databaseReference) {
if(databaseError == null){
// remove from adapter's list in GUI
RemoveUserFromList();
}else{
Toast.makeText(UsersAssignedActivity.this, "Could not delete database entries", Toast.LENGTH_SHORT).show();
}
}
});
}
});
builder.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int i) {
dialog.cancel();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
所以我想我会改用规则并检查约束服务器端。
我要编写满足以下条件的规则-
用户将无法分配给已满的班次。 (其numOfEmps
等于shift-assign/shiftKey
下的子节点数)
用户在某个日期不能分配超过一半的班次(如果与同一日期相关联的有4个班次,并且用户已经被分配到其中一个班次,则他们只能允许在该日期将自己分配给另外一个班次。)
用户连续不能工作超过4天。 (如果用户尝试在某个日期将自己分配给某个班次,并且在过去4天中已经将该用户分配给一个班次,则他们将无法为其分配自己的班次)
< / li>最后,如果尚未将用户分配给该班次。我猜这可以通过事务在客户端进行检查。
如何编写这些规则?
答案 0 :(得分:0)
您的问题中有一个非常广泛的用例。我将向您展示如何从第一个要求开始:
用户将无法分配到已满的班次。 (其numOfEmps等于shift-assign / shiftKey下的子节点数)
安全规则无法对数据执行任何汇总。由于此要求要求您比较两个值,因此两个值必须存储在数据库中规则内已知路径下。
您已经拥有numOfEmps
,但是您还需要存储已经注册进行轮班的人员数量。例如,您可以将注册人数添加到轮班中,然后在每次添加/删除操作时对其进行更新:
"shifts" : {
"key1" : {
"date" : "20191202",
"endTime" : "02:00",
"numOfEmps" : 4,
"numSignups" : 3,
"startTime" : "21:00",
"wage" : 30
},
现在您可以通过以下方式检查安全规则:
{
"rules": {
"shift-assigns": {
"$shiftid": {
"$uid": {
".validate": "root.child('/shifts/$shiftid/numSignups').val() < root.child('/shifts/$shiftid/numOfEmps').val()"
}
}
}
}
}
当然,您还需要确保用户:
$uid === auth.uid
之类的内容将自己添加到班次中/从班次中删除。numSignups
。尽管有一些涉及,但在安全规则中这是可能的。有关详细说明,请在这里查看我的答案:Is the way the Firebase database quickstart handles counts secure? 完成此操作后,您可以一个接一个地开始其他要求,直到满足所有要求为止。
总而言之,对于所有这些用例,您的安全规则将变得非常复杂。虽然您的要求似乎都可以实现,但是您将需要花费大量的时间来学习如何处理安全规则的局限性,以及如何在其中实现用例。
另一种方法是在Cloud Function中实现相同的逻辑,然后从应用程序代码中调用该逻辑。对于大多数开发人员而言,Cloud Functions更加自然,并且也是有效的选择。