fix(server): Phase 1.2 核心功能修复 — C1/C2/H4/H6
- feat(diary): 新增 list_all_classes 管理端 API (GET /diary/classes/all)
- feat(diary): 新增班级更新 API (PUT /diary/classes/{id}) — 名称/学校名编辑
- feat(diary): 新增班级停用 API (PATCH /diary/classes/{id}/deactivate)
- feat(diary): 新增班级码重置 API (POST /diary/classes/{id}/reset-code)
- fix(db): 补充权限 seed — student 获得 update/delete, teacher 获得 comment.delete
- refactor(diary): 删除 comment_service 中废弃的 contains_sensitive_words 死代码
- test(diary): 77 测试全部通过
This commit is contained in:
@@ -81,6 +81,26 @@ pub struct JoinClassReq {
|
|||||||
pub class_code: String,
|
pub class_code: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 更新班级请求
|
||||||
|
#[derive(Debug, Deserialize, ToSchema)]
|
||||||
|
pub struct UpdateClassReq {
|
||||||
|
/// 班级名称
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// 学校名称
|
||||||
|
pub school_name: Option<String>,
|
||||||
|
/// 乐观锁版本号
|
||||||
|
pub version: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重置班级码响应
|
||||||
|
#[derive(Debug, Serialize, ToSchema)]
|
||||||
|
pub struct ResetClassCodeResp {
|
||||||
|
/// 班级 ID
|
||||||
|
pub class_id: uuid::Uuid,
|
||||||
|
/// 新的 6 位班级码
|
||||||
|
pub new_class_code: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// 班级响应
|
/// 班级响应
|
||||||
#[derive(Debug, Serialize, ToSchema)]
|
#[derive(Debug, Serialize, ToSchema)]
|
||||||
pub struct ClassResp {
|
pub struct ClassResp {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use erp_core::error::AppError;
|
|||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
use erp_core::types::{ApiResponse, TenantContext};
|
use erp_core::types::{ApiResponse, TenantContext};
|
||||||
|
|
||||||
use crate::dto::{ClassMemberResp, ClassResp, CreateClassReq, JoinClassReq};
|
use crate::dto::{ClassMemberResp, ClassResp, CreateClassReq, JoinClassReq, ResetClassCodeResp, UpdateClassReq};
|
||||||
use crate::service::class_service::ClassService;
|
use crate::service::class_service::ClassService;
|
||||||
use crate::state::DiaryState;
|
use crate::state::DiaryState;
|
||||||
|
|
||||||
@@ -190,3 +190,152 @@ where
|
|||||||
let resp = ClassService::my_classes(ctx.tenant_id, ctx.user_id, &state.db).await?;
|
let resp = ClassService::my_classes(ctx.tenant_id, ctx.user_id, &state.db).await?;
|
||||||
Ok(Json(ApiResponse::ok(resp)))
|
Ok(Json(ApiResponse::ok(resp)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/diary/classes/all",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "成功", body = ApiResponse<Vec<ClassResp>>),
|
||||||
|
(status = 401, description = "未授权"),
|
||||||
|
(status = 403, description = "权限不足"),
|
||||||
|
),
|
||||||
|
security(("bearer_auth" = [])),
|
||||||
|
tag = "班级管理"
|
||||||
|
)]
|
||||||
|
/// GET /api/v1/diary/classes/all
|
||||||
|
///
|
||||||
|
/// 获取租户下所有班级(管理端用)。需要 `diary.class.manage` 权限。
|
||||||
|
pub async fn list_all_classes<S>(
|
||||||
|
State(state): State<DiaryState>,
|
||||||
|
Extension(ctx): Extension<TenantContext>,
|
||||||
|
) -> Result<Json<ApiResponse<Vec<ClassResp>>>, AppError>
|
||||||
|
where
|
||||||
|
DiaryState: FromRef<S>,
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
|
let resp = ClassService::list_all_classes(ctx.tenant_id, &state.db).await?;
|
||||||
|
Ok(Json(ApiResponse::ok(resp)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/api/v1/diary/classes/{id}",
|
||||||
|
params(("id" = Uuid, Path, description = "班级ID")),
|
||||||
|
request_body = UpdateClassReq,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "更新成功", body = ApiResponse<ClassResp>),
|
||||||
|
(status = 400, description = "验证失败"),
|
||||||
|
(status = 401, description = "未授权"),
|
||||||
|
(status = 403, description = "权限不足"),
|
||||||
|
(status = 404, description = "班级不存在"),
|
||||||
|
(status = 409, description = "版本冲突"),
|
||||||
|
),
|
||||||
|
security(("bearer_auth" = [])),
|
||||||
|
tag = "班级管理"
|
||||||
|
)]
|
||||||
|
/// PUT /api/v1/diary/classes/:id
|
||||||
|
///
|
||||||
|
/// 更新班级信息。需要 `diary.class.manage` 权限(仅班级创建者可编辑)。
|
||||||
|
pub async fn update_class<S>(
|
||||||
|
State(state): State<DiaryState>,
|
||||||
|
Extension(ctx): Extension<TenantContext>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(req): Json<UpdateClassReq>,
|
||||||
|
) -> Result<Json<ApiResponse<ClassResp>>, AppError>
|
||||||
|
where
|
||||||
|
DiaryState: FromRef<S>,
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
|
if let Some(ref name) = req.name {
|
||||||
|
if name.trim().is_empty() {
|
||||||
|
return Err(AppError::Validation("班级名称不能为空".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = ClassService::update_class(
|
||||||
|
ctx.tenant_id,
|
||||||
|
ctx.user_id,
|
||||||
|
id,
|
||||||
|
req.name,
|
||||||
|
req.school_name,
|
||||||
|
req.version,
|
||||||
|
&state.db,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(ApiResponse::ok(resp)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
patch,
|
||||||
|
path = "/api/v1/diary/classes/{id}/deactivate",
|
||||||
|
params(("id" = Uuid, Path, description = "班级ID")),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "停用成功", body = ApiResponse<ClassResp>),
|
||||||
|
(status = 401, description = "未授权"),
|
||||||
|
(status = 403, description = "权限不足"),
|
||||||
|
(status = 404, description = "班级不存在"),
|
||||||
|
),
|
||||||
|
security(("bearer_auth" = [])),
|
||||||
|
tag = "班级管理"
|
||||||
|
)]
|
||||||
|
/// PATCH /api/v1/diary/classes/:id/deactivate
|
||||||
|
///
|
||||||
|
/// 停用班级。需要 `diary.class.manage` 权限(仅班级创建者可停用)。
|
||||||
|
pub async fn deactivate_class<S>(
|
||||||
|
State(state): State<DiaryState>,
|
||||||
|
Extension(ctx): Extension<TenantContext>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<ApiResponse<ClassResp>>, AppError>
|
||||||
|
where
|
||||||
|
DiaryState: FromRef<S>,
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
|
let resp = ClassService::deactivate_class(
|
||||||
|
ctx.tenant_id,
|
||||||
|
ctx.user_id,
|
||||||
|
id,
|
||||||
|
&state.db,
|
||||||
|
&state.event_bus,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(ApiResponse::ok(resp)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/diary/classes/{id}/reset-code",
|
||||||
|
params(("id" = Uuid, Path, description = "班级ID")),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "重置成功", body = ApiResponse<ResetClassCodeResp>),
|
||||||
|
(status = 401, description = "未授权"),
|
||||||
|
(status = 403, description = "权限不足"),
|
||||||
|
(status = 404, description = "班级不存在"),
|
||||||
|
),
|
||||||
|
security(("bearer_auth" = [])),
|
||||||
|
tag = "班级管理"
|
||||||
|
)]
|
||||||
|
/// POST /api/v1/diary/classes/:id/reset-code
|
||||||
|
///
|
||||||
|
/// 重置班级码。需要 `diary.class.manage` 权限(仅班级创建者可重置)。
|
||||||
|
pub async fn reset_class_code<S>(
|
||||||
|
State(state): State<DiaryState>,
|
||||||
|
Extension(ctx): Extension<TenantContext>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<ApiResponse<ResetClassCodeResp>>, AppError>
|
||||||
|
where
|
||||||
|
DiaryState: FromRef<S>,
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
|
let resp = ClassService::reset_class_code(ctx.tenant_id, ctx.user_id, id, &state.db).await?;
|
||||||
|
Ok(Json(ApiResponse::ok(resp)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -142,18 +142,31 @@ impl DiaryModule {
|
|||||||
axum::routing::post(class_handler::create_class)
|
axum::routing::post(class_handler::create_class)
|
||||||
.get(class_handler::my_classes),
|
.get(class_handler::my_classes),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/diary/classes/all",
|
||||||
|
axum::routing::get(class_handler::list_all_classes),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/diary/classes/join",
|
"/diary/classes/join",
|
||||||
axum::routing::post(class_handler::join_class),
|
axum::routing::post(class_handler::join_class),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/diary/classes/{id}",
|
"/diary/classes/{id}",
|
||||||
axum::routing::get(class_handler::get_class),
|
axum::routing::get(class_handler::get_class)
|
||||||
|
.put(class_handler::update_class),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/diary/classes/{id}/members",
|
"/diary/classes/{id}/members",
|
||||||
axum::routing::get(class_handler::list_members),
|
axum::routing::get(class_handler::list_members),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/diary/classes/{id}/deactivate",
|
||||||
|
axum::routing::patch(class_handler::deactivate_class),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/diary/classes/{id}/reset-code",
|
||||||
|
axum::routing::post(class_handler::reset_class_code),
|
||||||
|
)
|
||||||
// 主题布置
|
// 主题布置
|
||||||
.route(
|
.route(
|
||||||
"/diary/classes/{class_id}/topics",
|
"/diary/classes/{class_id}/topics",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use sea_orm::{
|
|||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::dto::{ClassMemberResp, ClassResp};
|
use crate::dto::{ClassMemberResp, ClassResp, ResetClassCodeResp};
|
||||||
use crate::entity::{class_member, school_class};
|
use crate::entity::{class_member, school_class};
|
||||||
use crate::error::{DiaryError, DiaryResult};
|
use crate::error::{DiaryError, DiaryResult};
|
||||||
use erp_core::events::{DomainEvent, EventBus};
|
use erp_core::events::{DomainEvent, EventBus};
|
||||||
@@ -321,6 +321,172 @@ impl ClassService {
|
|||||||
Ok(classes.into_iter().map(class_model_to_resp).collect())
|
Ok(classes.into_iter().map(class_model_to_resp).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取租户下所有班级(管理端用)
|
||||||
|
///
|
||||||
|
/// 仅限管理员/老师角色调用,返回租户内所有未删除的班级。
|
||||||
|
pub async fn list_all_classes(
|
||||||
|
tenant_id: Uuid,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> DiaryResult<Vec<ClassResp>> {
|
||||||
|
let classes = school_class::Entity::find()
|
||||||
|
.filter(school_class::Column::TenantId.eq(tenant_id))
|
||||||
|
.filter(school_class::Column::DeletedAt.is_null())
|
||||||
|
.all(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(classes.into_iter().map(class_model_to_resp).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新班级信息(老师)
|
||||||
|
///
|
||||||
|
/// 仅班级创建者(teacher_id)可修改班级名称和学校名称。
|
||||||
|
/// 使用乐观锁防止并发冲突。
|
||||||
|
pub async fn update_class(
|
||||||
|
tenant_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
class_id: Uuid,
|
||||||
|
name: Option<String>,
|
||||||
|
school_name: Option<String>,
|
||||||
|
version: i32,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> DiaryResult<ClassResp> {
|
||||||
|
let model = school_class::Entity::find()
|
||||||
|
.filter(school_class::Column::Id.eq(class_id))
|
||||||
|
.filter(school_class::Column::TenantId.eq(tenant_id))
|
||||||
|
.filter(school_class::Column::DeletedAt.is_null())
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| DiaryError::NotFound(format!("班级 {} 不存在", class_id)))?;
|
||||||
|
|
||||||
|
// 仅班级创建者可编辑
|
||||||
|
if model.teacher_id != user_id {
|
||||||
|
return Err(DiaryError::Forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 乐观锁校验
|
||||||
|
if model.version != version {
|
||||||
|
return Err(DiaryError::VersionConflict {
|
||||||
|
local: version,
|
||||||
|
server: model.version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = Utc::now();
|
||||||
|
let mut active: school_class::ActiveModel = model.into();
|
||||||
|
if let Some(n) = name {
|
||||||
|
if n.trim().is_empty() {
|
||||||
|
return Err(DiaryError::Validation("班级名称不能为空".to_string()));
|
||||||
|
}
|
||||||
|
active.name = Set(n);
|
||||||
|
}
|
||||||
|
if let Some(s) = school_name {
|
||||||
|
active.school_name = Set(Some(s));
|
||||||
|
}
|
||||||
|
active.updated_at = Set(now);
|
||||||
|
active.updated_by = Set(user_id);
|
||||||
|
active.version = Set(version + 1);
|
||||||
|
|
||||||
|
let updated = active.update(db).await?;
|
||||||
|
Ok(class_model_to_resp(updated))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 停用班级(老师)
|
||||||
|
///
|
||||||
|
/// 将班级设为停用状态,学生将无法通过班级码加入。
|
||||||
|
/// 已在班内的学生仍可查看班级内容。
|
||||||
|
pub async fn deactivate_class(
|
||||||
|
tenant_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
class_id: Uuid,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
event_bus: &EventBus,
|
||||||
|
) -> DiaryResult<ClassResp> {
|
||||||
|
let model = school_class::Entity::find()
|
||||||
|
.filter(school_class::Column::Id.eq(class_id))
|
||||||
|
.filter(school_class::Column::TenantId.eq(tenant_id))
|
||||||
|
.filter(school_class::Column::DeletedAt.is_null())
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| DiaryError::NotFound(format!("班级 {} 不存在", class_id)))?;
|
||||||
|
|
||||||
|
// 仅班级创建者可停用
|
||||||
|
if model.teacher_id != user_id {
|
||||||
|
return Err(DiaryError::Forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.is_active {
|
||||||
|
return Err(DiaryError::BadRequest("班级已处于停用状态".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = Utc::now();
|
||||||
|
let current_version = model.version;
|
||||||
|
let mut active: school_class::ActiveModel = model.into();
|
||||||
|
active.is_active = Set(false);
|
||||||
|
active.updated_at = Set(now);
|
||||||
|
active.updated_by = Set(user_id);
|
||||||
|
active.version = Set(current_version + 1);
|
||||||
|
let updated = active.update(db).await?;
|
||||||
|
|
||||||
|
// 发布 ClassDeactivated 事件
|
||||||
|
event_bus
|
||||||
|
.publish(
|
||||||
|
DomainEvent::new(
|
||||||
|
"diary.class.deactivated",
|
||||||
|
tenant_id,
|
||||||
|
serde_json::json!({
|
||||||
|
"class_id": class_id,
|
||||||
|
"teacher_id": user_id,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
db,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(class_model_to_resp(updated))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重置班级码(老师)
|
||||||
|
///
|
||||||
|
/// 生成新的 6 位班级码,旧码立即失效。
|
||||||
|
/// CLAUDE.md 要求:"老师可随时重置"。
|
||||||
|
pub async fn reset_class_code(
|
||||||
|
tenant_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
class_id: Uuid,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> DiaryResult<ResetClassCodeResp> {
|
||||||
|
let model = school_class::Entity::find()
|
||||||
|
.filter(school_class::Column::Id.eq(class_id))
|
||||||
|
.filter(school_class::Column::TenantId.eq(tenant_id))
|
||||||
|
.filter(school_class::Column::DeletedAt.is_null())
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| DiaryError::NotFound(format!("班级 {} 不存在", class_id)))?;
|
||||||
|
|
||||||
|
// 仅班级创建者可重置班级码
|
||||||
|
if model.teacher_id != user_id {
|
||||||
|
return Err(DiaryError::Forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_code = Self::generate_unique_code(db).await?;
|
||||||
|
let now = Utc::now();
|
||||||
|
let current_version = model.version;
|
||||||
|
|
||||||
|
let mut active: school_class::ActiveModel = model.into();
|
||||||
|
active.class_code = Set(new_code.clone());
|
||||||
|
// 重置过期时间为 6 个月
|
||||||
|
active.expires_at = Set(now.checked_add_months(Months::new(6)));
|
||||||
|
active.updated_at = Set(now);
|
||||||
|
active.updated_by = Set(user_id);
|
||||||
|
active.version = Set(current_version + 1);
|
||||||
|
active.update(db).await?;
|
||||||
|
|
||||||
|
Ok(ResetClassCodeResp {
|
||||||
|
class_id,
|
||||||
|
new_class_code: new_code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// 生成唯一班级码(重试最多 10 次)
|
/// 生成唯一班级码(重试最多 10 次)
|
||||||
async fn generate_unique_code(db: &DatabaseConnection) -> DiaryResult<String> {
|
async fn generate_unique_code(db: &DatabaseConnection) -> DiaryResult<String> {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
|
|||||||
@@ -200,41 +200,20 @@ fn comment_model_to_resp(model: comment::Model) -> CommentResp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 基础敏感词检查
|
|
||||||
///
|
|
||||||
/// Phase 1 使用简单字符串匹配,B6 阶段替换为 ContentSafetyService。
|
|
||||||
fn contains_sensitive_words(content: &str) -> bool {
|
|
||||||
const SENSITIVE_WORDS: &[&str] = &[
|
|
||||||
// 占位 — Phase 1 仅检查是否为空或过短
|
|
||||||
// 完整词库将在 B6 ContentSafetyService 中添加
|
|
||||||
];
|
|
||||||
|
|
||||||
if content.trim().is_empty() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for word in SENSITIVE_WORDS {
|
|
||||||
if content.contains(word) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_content_is_sensitive() {
|
fn content_safety_phase1_empty_is_safe() {
|
||||||
assert!(contains_sensitive_words(""));
|
// Phase 1 词库为空,所有内容(包括空串)返回 Safe
|
||||||
assert!(contains_sensitive_words(" "));
|
// 空内容检查由 handler 层的 Validation 守卫处理
|
||||||
|
assert!(ContentSafetyService::is_safe(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normal_content_is_not_sensitive() {
|
fn normal_content_is_safe() {
|
||||||
assert!(!contains_sensitive_words("今天天气真好!"));
|
assert!(ContentSafetyService::is_safe("今天天气真好!"));
|
||||||
assert!(!contains_sensitive_words("老师点评:写得不错"));
|
assert!(ContentSafetyService::is_safe("老师点评:写得不错"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,17 +33,23 @@ impl MigrationTrait for Migration {
|
|||||||
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// student 权限: diary.journal.create, diary.journal.read
|
// student 权限: diary.journal.create, diary.journal.read, diary.journal.update, diary.journal.delete
|
||||||
// teacher 权限: diary.journal.create, diary.journal.read, diary.class.manage, diary.topic.assign, diary.comment.write
|
// teacher 权限: diary.journal.create, diary.journal.read, diary.journal.update, diary.journal.delete,
|
||||||
|
// diary.class.manage, diary.topic.assign, diary.comment.write, diary.comment.delete
|
||||||
// parent 权限: diary.journal.read, diary.parent.bind
|
// parent 权限: diary.journal.read, diary.parent.bind
|
||||||
let role_permissions = [
|
let role_permissions = [
|
||||||
("student", "diary.journal.create"),
|
("student", "diary.journal.create"),
|
||||||
("student", "diary.journal.read"),
|
("student", "diary.journal.read"),
|
||||||
|
("student", "diary.journal.update"),
|
||||||
|
("student", "diary.journal.delete"),
|
||||||
("teacher", "diary.journal.create"),
|
("teacher", "diary.journal.create"),
|
||||||
("teacher", "diary.journal.read"),
|
("teacher", "diary.journal.read"),
|
||||||
|
("teacher", "diary.journal.update"),
|
||||||
|
("teacher", "diary.journal.delete"),
|
||||||
("teacher", "diary.class.manage"),
|
("teacher", "diary.class.manage"),
|
||||||
("teacher", "diary.topic.assign"),
|
("teacher", "diary.topic.assign"),
|
||||||
("teacher", "diary.comment.write"),
|
("teacher", "diary.comment.write"),
|
||||||
|
("teacher", "diary.comment.delete"),
|
||||||
("parent", "diary.journal.read"),
|
("parent", "diary.journal.read"),
|
||||||
("parent", "diary.parent.bind"),
|
("parent", "diary.parent.bind"),
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user