学生情報管理システム#
システム機能説明#
学生情報管理システムはB/S モデルに基づいて開発されたシステムです。システムには学生、教師、管理者の 3 つの役割が含まれており、ユーザーのカスタム追加も可能です。また、管理者の権限の下で、権限の割り当て機能を通じてユーザー権限管理を精密化し、ある程度学生情報管理システムの安全性を確保しています。
学生#
-
学生はクラスメートの関連情報(パスワードなどのプライバシー要素は暗号化されています、非 ** 管理者ユーザーは閲覧権限がありません)を確認でき、自身の情報を修正したり、自分の記録を削除したり、新たに個人情報の記録を追加することができます。
-
学生は教師チームや関連クラスを確認し、どのクラスが授業を受けているか、どの教師が教えているかなどの関連情報を把握できます。
教師#
-
教師はクラスの学生に関する情報を確認でき、クラスの学生に関する情報を追加、削除、修正、検索(ログインパスワードの変更を含む)することができ、学生のログイン体験をある程度最適化できます。
-
教師は同僚の授業状況や自分が担当するクラスを確認し、同僚の関連情報を理解することができます。
-
教師は操作履歴を確認することができ、ある程度追跡可能で、システムの安定性と安全性を向上させます。
スーパーユーザー#
- スーパーユーザーは教師と学生のすべての機能を持ち、その上でメニュー管理機能を持ち、
Tree
形式で全体の管理システムを縦にブラウジングでき、システムのメンテナンスを容易にします。
- スーパーユーザーは辞書を管理でき、バックエンド管理システムを通じて、辞書識別子、辞書タイプ、辞書値、作成時間、更新時間などを詳細に把握でき、追加、削除、修正、検索をサポートします。
-
本プロジェクトは二次開発をサポートしており、管理者が一定のニーズを持つ場合、開発センターモジュールを通じて本システムを二次開発できます。
システム機能モジュール図#
システムデータベース設計#
sys_action_log.sql#
管理システムの操作ログを記録します。
sys_dept.sql#
部門管理に関連するデータを保存します。
sys_dict.sql#
メニュー管理に関連するデータを保存します。
sys_file.sql#
アバターのアップロードに関連するデータを保存します。
sys_menu.sql#
システムメニューに関連するデータを保存します。
sys_role.sql#
システムロールに関連するデータを保存します。
sys_role_menu.sql#
システムロール IDに関連するデータを保存します。
sys_user.sql#
システムユーザーに関連するデータを保存します。
sys_user_role.sql#
システムユーザー IDに関連するデータを保存します。
システムの具体的な実装#
全体機能モジュール図#
ログインモジュール#
H5 ページ図#
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="/common/template :: header(~{::title},~{::link},~{::style})">
<title>TIMOログイン</title>
<link rel="stylesheet" type="text/css" th:href="@{/css/login.css}">
</head>
<body class="layui-layout-login">
<div class="login-bg">
<div class="cover"></div>
</div>
<div class="login-content" th:th:classappend="${isCaptcha} ? 'captcha'">
<h1 class="login-box-title"><i class="fa fa-fw fa-user"></i>ログイン</h1>
<form class="layui-form" th:action="@{/login}" method="post">
<div class="layui-form-item">
<label class="layui-icon layui-icon-username" for="username"></label>
<input class="layui-input" type="text" name="username" id="username" placeholder="ユーザー名">
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password" for="password"></label>
<input class="layui-input" type="password" name="password" id="password" placeholder="パスワード">
</div>
<div th:if="${isCaptcha}" class="layui-form-item captcha-item">
<label class="layui-icon layui-icon-vercode"></label>
<input class="layui-input" type="text" name="captcha" autocomplete="off" placeholder="認証コード">
<img class="captcha-img" th:src="@{/captcha}" />
</div>
<div class="layui-form-item">
<input type="checkbox" name="rememberMe" title="ログイン情報を記憶する" lay-skin="primary">
<a class="layui-layout-right forget-password" href="javascript:alert('スーパーユーザーに連絡してください!')">パスワードを忘れましたか?</a>
</div>
<button type="submit" class="layui-btn layui-btn-fluid ajax-login"><i class="fa fa-sign-in fa-lg fa-fw"></i> ログイン</button>
</form>
<div class="layui-layer-loading login-page-loading"><div class="layui-layer-content"></div></div>
</div>
<script th:replace="/common/template :: script"></script>
<script th:src="@{/js/login.js}" charset="utf-8"></script>
</body>
</html>
js コアコード#
if(window.top!==window.self){window.top.location=window.location};
layui.use(['element'], function () {
var $ = layui.jquery;
$(document).on('click', '.captcha-img', function () {
var src = this.src.split("?")[0];
this.src = src + "?" + Math.random();
});
$(document).on('click', '.ajax-login', function (e) {
e.preventDefault();
var form = $(this).parents("form");
var url = form.attr("action");
var serializeArray = form.serializeArray();
$.post(url, serializeArray, function (result) {
if(result.code !== 200){
$('.captcha-img').click();
}
$.fn.Messager(result);
});
});
$('.layui-layer-loading').hide();
});
css スタイルシート#
@charset "UTF-8";
.layui-layout-login .login-bg{
background-color: #e7e7e7;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
.layui-layout-login .login-bg .cover{
background-color: #009688;
height: 50%;
}
.layui-layout-login .login-content{
width:250px;
position: absolute;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
background-color: #ffffff;
padding: 40px;
padding-top: 32px;
-webkit-box-shadow: 0px 3px 20px 3px rgba(0, 0, 0, 0.15);
box-shadow: 0px 3px 20px 3px rgba(0, 0, 0, 0.15);
}
.layui-layout-login .login-content.captcha{
width:300px;
padding-bottom: 38px;
}
.layui-layout-login .login-content.captcha .captcha-item .layui-icon{
font-size: 16px;
}
.layui-layout-login .login-content.captcha .captcha-item .layui-input{
float: left;
width: 180px;
}
.layui-layout-login .login-content.captcha .captcha-item .captcha-img{
float: right;
height: 38px;
cursor: pointer;
}
.layui-layout-login .login-box-title{
font-size: 26px;
margin-bottom: 20px;
text-align: center;
color: #444444;
}
.layui-layout-login .layui-form-item{
position: relative;
margin-bottom: 20px;
min-height: 18px;
}
.layui-layout-login .layui-form-item label{
position: absolute;
top:0;
left: 0;
font-size: 18px;
width: 38px;
line-height: 38px;
text-align: center;
color: #999999;
}
.layui-layout-login .layui-form-item input[type=text],
.layui-layout-login .layui-form-item input[type=password]{
padding-left: 36px;
border: 1px solid #ddd;
transition: all 0s;
-webkit-transition: all 0s;
}
.layui-layout-login .layui-form-item .layui-form-checkbox{
margin-top: 0;
}
.layui-layout-login .layui-form-item .layui-form-checkbox .layui-icon{
width: 14px;
height: 14px;
top: 1px;
line-height: 14px;
}
.layui-layout-login .layui-form-item .layui-form-checkbox[lay-skin=primary]:hover i {
border-color: #009688;
}
.layui-layout-login .layui-form-item .layui-form-checked[lay-skin=primary] i{
border-color: #009688;
background-color: #009688;
}
.layui-layout-login .layui-form-item .forget-password{
color: #009688;
}
.layui-layout-login .login-page-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0!important;
background-color: rgba(255, 255, 255, 0.3)!important;
}
ビジネスコード#
package com.linln.admin.system.controller;
import com.linln.common.config.properties.ProjectProperties;
import com.linln.common.data.URL;
import com.linln.common.enums.ResultEnum;
import com.linln.common.exception.ResultException;
import com.linln.common.utils.CaptchaUtil;
import com.linln.common.utils.ResultVoUtil;
import com.linln.common.utils.SpringContextUtil;
import com.linln.common.vo.ResultVo;
import com.linln.component.actionLog.action.UserAction;
import com.linln.component.actionLog.annotation.ActionLog;
import com.linln.component.shiro.ShiroUtil;
import com.linln.modules.system.domain.User;
import com.linln.modules.system.service.RoleService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 小懒虫
* @date 2018/8/14
*/
@Controller
public class LoginController implements ErrorController {
@Autowired
private RoleService roleService;
/**
* ログインページに遷移
*/
@GetMapping("/login")
public String toLogin(Model model) {
ProjectProperties properties = SpringContextUtil.getBean(ProjectProperties.class);
model.addAttribute("isCaptcha", properties.isCaptchaOpen());
return "/login";
}
/**
* ログインを実現
*/
@PostMapping("/login")
@ResponseBody
@ActionLog(key = UserAction.USER_LOGIN, action = UserAction.class)
public ResultVo login(String username, String password, String captcha, String rememberMe) {
// アカウントとパスワードが空でないか確認
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
throw new ResultException(ResultEnum.USER_NAME_PWD_NULL);
}
// 認証コードが正しいか確認
ProjectProperties properties = SpringContextUtil.getBean(ProjectProperties.class);
if (properties.isCaptchaOpen()) {
Session session = SecurityUtils.getSubject().getSession();
String sessionCaptcha = (String) session.getAttribute("captcha");
if (StringUtils.isEmpty(captcha) || StringUtils.isEmpty(sessionCaptcha)
|| !captcha.toUpperCase().equals(sessionCaptcha.toUpperCase())) {
throw new ResultException(ResultEnum.USER_CAPTCHA_ERROR);
}
session.removeAttribute("captcha");
}
// 1. Subject主体オブジェクトを取得
Subject subject = SecurityUtils.getSubject();
// 2. ユーザーデータを封装
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 3. ログインを実行し、カスタムRealmクラスに入る
try {
// 自動ログインかどうか確認
if (rememberMe != null) {
token.setRememberMe(true);
} else {
token.setRememberMe(false);
}
subject.login(token);
// バックエンドロールを持っているか確認
User user = ShiroUtil.getSubject();
if (roleService.existsUserOk(user.getId())) {
return ResultVoUtil.success("ログイン成功", new URL("/"));
} else {
SecurityUtils.getSubject().logout();
return ResultVoUtil.error("あなたはバックエンド管理者ではありません!");
}
} catch (LockedAccountException e) {
return ResultVoUtil.error("このアカウントは凍結されています");
} catch (AuthenticationException e) {
return ResultVoUtil.error("ユーザー名またはパスワードが間違っています");
}
}
/**
* 認証コード画像
*/
@GetMapping("/captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
// レスポンスヘッダー情報を設定し、ブラウザにキャッシュしないよう通知
response.setHeader("Expires", "-1");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "-1");
response.setContentType("image/jpeg");
// 認証コードを取得
String code = CaptchaUtil.getRandomCode();
// 認証コードをセッションに入力し、検証に使用
request.getSession().setAttribute("captcha", code);
// ウェブページに出力
ImageIO.write(CaptchaUtil.genCaptcha(code), "jpg", response.getOutputStream());
}
/**
* ログアウト
*/
@GetMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
return "redirect:/login";
}
/**
* 権限不足ページ
*/
@GetMapping("/noAuth")
public String noAuth() {
return "/system/main/noAuth";
}
/**
* エラーページを処理
*/
@RequestMapping("${server.error.path:${error.path:/error}}")
public String handleError(Model model, HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
String errorMsg = "何かエラーが発生したようです!";
if (statusCode == 404) {
errorMsg = "ページが見つかりませんでした!火星に行ったようです~";
}
model.addAttribute("statusCode", statusCode);
model.addAttribute("msg", errorMsg);
return "/system/main/error";
}
}
実行スクリーンショット#
ユーザーモジュール#
定義#
ユーザーモジュールには管理者ログイン、学生ログイン、教師ログイン機能が含まれ、異なるユーザーが異なる役割を選択してシステムにログインし、異なる権限を享受します。また、パスワードなどの情報は暗号化され、非管理者は他のユーザー情報を閲覧できません。権限の割り当て機能を通じてユーザー権限管理を精密化し、ある程度学生情報管理システムの安全性を確保しています。学生はクラスメートの関連情報(パスワードなどのプライバシー要素は暗号化されています、非管理者ユーザーは閲覧権限がありません)を確認でき、自身の情報を修正したり、自分の記録を削除したり、新たに個人情報の記録を追加することができます。
機能紹介#
学生管理システムのユーザーモジュールは、システム内のユーザーアカウントと権限を管理するためのモジュールで、主に以下の機能を含みます:
- ユーザー登録:新しいユーザーは登録機能を通じてシステム内にアカウントを作成し、ユーザー名、パスワードなどの必須情報を入力します。また、必要に応じて名前、連絡先などの他の個人情報を入力できます。
- ユーザーログイン:登録済みのユーザーはログイン機能を通じてシステムに入ることができ、自分のアカウントとパスワードを使用して身分を確認します。ログイン成功後、ユーザーはシステム内の他の機能を使用できます。
- ユーザー情報管理:ログイン済みのユーザーは自分の個人情報を確認し、修正することができます(連絡先の変更など)。
- 権限管理:システム管理者は異なるユーザーの権限を設定でき、ユーザーの操作権限やデータアクセス権限などを管理できます。管理者は必要に応じて異なる権限レベルを設定し、システムの安全性とデータの機密性を確保します。
- ユーザー検索:ログイン済みのユーザーはシステム内の検索機能を使用して、自分の個人情報や関連記録(成績検索、選択科目の記録など)を検索できます。
- ユーザー退会:ログイン済みのユーザーは退会機能を通じてシステムから退出し、自分のアカウントの安全を保護できます。
コアコード(実装クラス)#
package com.linln.modules.system.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.linln.common.enums.StatusEnum;
import com.linln.common.utils.StatusUtil;
import com.linln.component.excel.annotation.Excel;
import com.linln.component.excel.enums.ExcelType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@Data
@Entity
@Table(name = "sys_user")
@ToString(exclude = {"dept", "roles"})
@EqualsAndHashCode(exclude = {"dept", "roles"})
@EntityListeners(AuditingEntityListener.class)
@SQLDelete(sql = "update sys_user" + StatusUtil.SLICE_DELETE)
@Where(clause = StatusUtil.NOT_DELETE)
@Excel("ユーザーデータ")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Excel(value = "ユーザーID", type = ExcelType.EXPORT)
private Long id;
@Excel("ユーザー名")
private String username;
@JsonIgnore
private String password;
@JsonIgnore
private String salt;
@Excel("ニックネーム")
private String nickname;
private String picture;
@Excel(value = "性別", dict = "USER_SEX")
private Byte sex;
@Excel("電話番号")
private String phone;
@Excel("電子メール")
private String email;
@CreatedDate
@Excel("作成時間")
private Date createDate;
@LastModifiedDate
@Excel("更新時間")
private Date updateDate;
@Excel("備考")
private String remark;
@Excel(value = "状態", dict = "DATA_STATUS")
private Byte status = StatusEnum.OK.getCode();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dept_id")
@JsonIgnore
private Dept dept;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "sys_user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
@JsonIgnore
private Set<Role> roles = new HashSet<>(0);
}
コアコード(インターフェース)#
package com.linln.modules.system.repository;
import com.linln.modules.system.domain.Dept;
import com.linln.modules.system.domain.User;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface UserRepository extends BaseRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* ユーザー名でユーザーデータを検索
* @param username ユーザー名
* @return ユーザーデータ
*/
public User findByUsername(String username);
/**
* ユーザー名でユーザーデータを検索し、指定IDのユーザーを除外
* @param username ユーザー名
* @param id 除外するユーザーID
* @return ユーザーデータ
*/
public User findByUsernameAndIdNot(String username, Long id);
/**
* 複数の該当部門のユーザーリストを検索
* @param dept 部門オブジェクト
* @return ユーザーリスト
*/
public List<User> findByDept(Dept dept);
/**
* 複数のデータを削除
* @param ids IDリスト
* @return 影響行数
*/
public Integer deleteByIdIn(List<Long> ids);
}
実行スクリーンショット#
メニューモジュール#
定義#
学生管理システムのメニューモジュールは、システム内で提供されるメニューバーで、ユーザーがシステム内の各機能モジュールを便利にブラウジングし、使用するためのものです。
機能#
- メニュー項目の分類:システム内の各機能モジュールは、学生情報管理、教師情報管理、コース管理、成績管理など、異なる分類に従って整理され、ユーザーが検索しやすくなります。
- メニュー項目の並べ替え:システム内のメニュー項目は、ユーザーの使用頻度や重要度に従って並べ替えられ、ユーザーが必要な機能を見つけやすくします。
- メニュー項目の展開:システム内ではメニュー項目の展開と折りたたみ機能が提供され、特定の分類のメニュー項目が多すぎる場合、部分的にメニュー項目を折りたたむことで、ユーザーの視覚的負担を軽減します。
- メニュー項目のアイコン:システム内のメニュー項目にはアイコンを追加でき、ユーザーがアイコンを基に機能を識別し、使用体験を向上させます。
- メニュー項目の権限制御:異なるユーザーに対して、システム内のメニュー項目の表示と可用性は異なる場合があり、ユーザーの権限に基づいて制御する必要があります。これにより、システムの安全性とデータの機密性が保証されます。
- メニュー項目の検索:システム内のメニュー項目が多すぎる場合、ユーザーはメニュー項目の検索機能を使用して、キーワードや名前を入力することで必要な機能モジュールを検索し、使用効率を向上させます。
- メニュー項目のショートカットキー:頻繁に使用される機能モジュールに対して、システムはショートカットキーを設定し、ユーザーがキーボード操作で迅速に対応する機能モジュールにアクセスできるようにします。
- メニュー項目のカスタマイズ:システム内のメニュー項目は、ユーザーのニーズに応じてカスタマイズでき、ユーザーは自分が頻繁に使用するメニュー項目をショートカットメニューに追加し、迅速にアクセスできるようにします。
- メニュー項目の国際化:国際化された学生管理システムの場合、メニュー項目はユーザーの言語環境に基づいて自動的に切り替わり、国際的な使用の便利さとユーザー体験を向上させます。
- メニュー項目のグループ化:大規模な学生管理システムの場合、メニュー項目は異なるグループに分類され、学生管理、教師管理、コース管理など、ユーザーが管理しやすく、使用しやすくなります。
- メニュー項目の拡張:学生管理システムに新しい機能モジュールを追加する必要がある場合、メニューモジュールは拡張機能を提供し、管理者が新しいメニュー項目や機能モジュールを簡単に追加できるようにします。
コアコード(実装クラス)#
package com.linln.modules.system.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.linln.common.enums.StatusEnum;
import com.linln.common.utils.StatusUtil;
import lombok.*;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.Where;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.*;
@Data
@Entity
@Table(name = "sys_menu")
@ToString(exclude = {"roles", "createBy", "updateBy"})
@EqualsAndHashCode(exclude = {"roles", "createBy", "updateBy"})
@EntityListeners(AuditingEntityListener.class)
@Where(clause = StatusUtil.NOT_DELETE)
public class Menu implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long pid;
private String pids;
private String title;
private String url;
private String perms;
private String icon;
private Byte type;
private Integer sort;
private String remark;
@CreatedDate
private Date createDate;
@LastModifiedDate
private Date updateDate;
@CreatedBy
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "create_by")
@JsonIgnore
private User createBy;
@LastModifiedBy
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "update_by")
@JsonIgnore
private User updateBy;
private Byte status = StatusEnum.OK.getCode();
@ManyToMany(mappedBy = "menus")
@JsonIgnore
private Set<Role> roles = new HashSet<>(0);
@Transient
@JsonIgnore
private Map<Long, Menu> children = new HashMap<>();
public Menu(){
}
public Menu(Long id, String title, String pids) {
this.id = id;
this.title = title;
this.pids = pids;
}
public void setPids(String pids) {
if (pids.startsWith(",")){
pids = pids.substring(1);
}
this.pids = pids;
}
}
コアコード(インターフェース)#
package com.linln.modules.system.repository;
import com.linln.common.constant.StatusConst;
import com.linln.modules.system.domain.Menu;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface MenuRepository extends BaseRepository<Menu, Long> {
/**
* 複数のメニューを検索
* @param ids idリスト
* @return メニューリスト
*/
public List<Menu> findByIdIn(List<Long> ids);
/**
* 該当する状態のメニューを検索
* @param sort ソートオブジェクト
* @param status データ状態
* @return メニューリスト
*/
public List<Menu> findAllByStatus(Sort sort, Byte status);
/**
* メニューURLを検索
* @param url idリスト
* @return メニュー情報
*/
public Menu findByUrl(String url);
/**
* 親IDで子メニューを検索
* @param pids pidリスト
* @param status データ状態
* @return メニューリスト
*/
public List<Menu> findByPidsLikeAndStatus(String pids, Byte status);
/**
* ソートの最大値を取得
* @param pid 親メニューID
* @return 最大値
*/
@Query("select max(sort) from Menu m where m.pid = ?1 and m.status <> " + StatusConst.DELETE)
public Integer findSortMax(long pid);
/**
* 親メニューIDに基づいて本級のすべてのメニューを取得
* @param sort ソートオブジェクト
* @param pid 親メニューID
* @param notId 除外するメニューID
* @return メニューリスト
*/
public List<Menu> findByPidAndIdNot(Sort sort, long pid, long notId);
/**
* メニューとロールの関係を解除
* @param id メニューID
* @return 影響行数
*/
@Modifying
@Transactional
@Query(value = "DELETE FROM sys_role_menu WHERE menu_id = ?1", nativeQuery = true)
public Integer cancelRoleJoin(Long id);
}
実行スクリーンショット#
部門モジュール#
定義#
学生管理システムの部門モジュールは、システム内の部門情報を管理するためのモジュールです。
機能#
- 部門情報の維持:システム管理者は部門モジュールを通じて部門情報を追加、編集、削除でき、部門名、説明、責任者などの情報を含みます。
- 部門メンバー管理:管理者は部門モジュール内で部門メンバーの情報を追加、編集、削除でき、名前、職位、連絡先などの情報を含みます。また、管理者は 1 人または複数のメンバーを 1 つまたは複数の部門に割り当てることができます。
- 部門権限管理:管理者は部門モジュールを通じて各部門に対してアクセス権限や操作権限などの権限を設定できます。これにより、部門間のデータの安全性と隔離が保証されます。
- 部門データ検索:ログイン済みのユーザーは部門モジュールが提供する検索機能を使用して、自分が所属する部門の情報や関連記録(部門メンバー情報、部門プロジェクト進捗など)を検索できます。
- 部門レポート生成:部門モジュールを通じて、管理者は部門に関連するレポート(メンバー構成レポート、プロジェクト進捗レポートなど)を生成できます。これらのレポートは、管理者が部門の状況をよりよく理解し、管理するのに役立ちます。
- 部門協力:部門モジュールを通じて、異なる部門間で協力が可能で、ファイルの共有、情報の交流、プロジェクトの共同実施などを行い、作業効率と協調能力を向上させます。
- 部門ログ記録:部門モジュールを通じて、システムは部門に関連する操作ログを記録でき、部門情報の追加、編集、削除の履歴を管理者が把握し、問題を追跡するのに役立ちます。
コアコード(実装クラス)#
package com.linln.modules.system.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.linln.common.enums.StatusEnum;
import com.linln.common.utils.StatusUtil;
import lombok.Data;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.Where;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Data
@Entity
@Table(name = "sys_dept")
@EntityListeners(AuditingEntityListener.class)
@Where(clause = StatusUtil.NOT_DELETE)
public class Dept implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/** 部門名 */
private String title;
/** 親番号 */
private Long pid;
/** すべての親番号 */
private String pids;
/** ソート */
private Integer sort;
/** 備考 */
private String remark;
/** 作成時間 */
@CreatedDate
private Date createDate;
/** 更新時間 */
@LastModifiedDate
private Date updateDate;
/** 作成者 */
@CreatedBy
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "create_by")
@JsonIgnore
private User createBy;
/** 更新者 */
@LastModifiedBy
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "update_by")
@JsonIgnore
private User updateBy;
/** データ状態 */
private Byte status = StatusEnum.OK.getCode();
}
コアコード(インターフェース)#
package com.linln.modules.system.repository;
import com.linln.common.constant.StatusConst;
import com.linln.modules.system.domain.Dept;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface DeptRepository extends BaseRepository<Dept, Long> {
/**
* 複数の部門を検索
* @param ids idリスト
* @return 部門リスト
*/
public List<Dept> findByIdIn(List<Long> ids);
/**
* ソートの最大値を取得
* @param pid 親部門ID
* @return 最大値
*/
@Query("select max(sort) from Menu m where m.pid = ?1 and m.status <> " + StatusConst.DELETE)
public Integer findSortMax(long pid);
/**
* 親IDで子孫部門を検索
* @param pids pidリスト
* @param status データ状態
* @return 部門リスト
*/
public List<Dept> findByPidsLikeAndStatus(String pids, Byte status);
/**
* 親部門IDに基づいて本級のすべての部門を取得
* @param sort ソートオブジェクト
* @param pid 親部門ID
* @param notId 除外する部門ID
* @return 部門リスト
*/
public List<Dept> findByPidAndIdNot(Sort sort, long pid, long notId);
}
実行スクリーンショット#
ログモジュール#
定義#
学生管理システムのログモジュールは、システム内のさまざまな操作のログ情報を記録するためのものです。
機能#
- ログ記録:システム管理者はログモジュールを通じて、ユーザーのログイン、データの変更、システム設定など、システム内のさまざまな操作を記録できます。これにより、管理者はシステムの運用状況や問題を把握できます。
- ログ検索:ログイン済みのユーザーはログモジュールが提供する検索機能を使用して、自分または他のユーザーの操作記録を検索でき、自分または他のユーザーの操作履歴や行動を把握できます。
- ログ分析:ログモジュールを通じて、管理者はシステム内の操作ログを分析し、システムの使用状況や問題を把握できます。たとえば、ユーザーのログイン回数やデータ変更回数などを統計分析し、システムの安全性や安定性を評価できます。
- ログバックアップ:システムデータの安全性を確保するために、ログモジュールはバックアップ機能を提供し、定期的にシステム内の操作ログをバックアップし、システムに問題が発生した場合にデータを復元し、操作履歴を追跡できます。
- ログレポート生成:ログモジュールを通じて、管理者はシステム操作ログのレポートを生成でき、操作記録の管理や分析に役立ちます。レポートは異なる時間帯、ユーザー、操作タイプなどに基づいて分類および統計分析できます。
- ログ監査:ログモジュールを通じて、管理者はシステム内の操作ログを監査し、潜在的な安全問題や脆弱性を発見し、解決できます。
- ログフィルタリング:ログモジュールを通じて、管理者は異なるキーワードに基づいてフィルタリングでき、特定の操作記録を選別できます。たとえば、ユーザー名、操作タイプ、時間範囲などに基づいてフィルタリングできます。
- ログの安全性:ログ記録の安全性とプライバシーを確保するために、ログモジュールは暗号化、権限制御などの安全対策を提供し、未承認のユーザーによる不正操作や操作記録の改ざんを防ぎます。
コアコード(実装クラス)#
package com.linln.modules.system.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Data
@Entity
@Table(name="sys_action_log")
@EntityListeners(AuditingEntityListener.class)
public class ActionLog implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Byte type;
private String ipaddr;
private String clazz;
private String method;
private String model;
private Long recordId;
@Lob @Column(columnDefinition="TEXT")
private String message;
@CreatedDate
private Date createDate;
@ManyToOne(fetch=FetchType.LAZY)
@NotFound(action=NotFoundAction.IGNORE)
@JoinColumn(name="oper_by")
@JsonIgnore
private User operBy;
private String operName;
public ActionLog(){}
/**
* ログオブジェクトを封装
* @param name ログ名
* @param message ログメッセージ
*/
public ActionLog(String name, String message){
this.name = name;
this.message = message;
}
}
コアコード(インターフェース)#
package com.linln.modules.system.repository;
import com.linln.modules.system.domain.ActionLog;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ActionLogRepository extends JpaRepository<ActionLog, Long> {
/**
* モデルとデータIDに基づいてログリストを検索
* @param model モデル(テーブル名)
* @param recordId データID
* @return ログリスト
*/
public List<ActionLog> findByModelAndRecordId(String model, Long recordId);
}
実行スクリーンショット#
実習まとめ#
Spring Boot
はオープンソースのフレームワークで、Java
アプリケーションの作成、デプロイ、実行を簡素化することを目的としています。その主な目的は、生産レベルのアプリケーションを迅速に構築し、設定ファイルの数を減らし、設定プロセスを簡素化することです。Spring Boot
を使用することで、開発者は Web アプリケーションを迅速に構築し、内蔵のTomcat
サーバーを使用して、手動で何も設定することなく、すぐに起動できます。
実習では、Spring Boot
を使用してWeb
アプリケーションを構築する方法、Spring MVC
を使用してRESTful Web
サービスを構築する方法、Spring Data JPA
を使用してデータ永続化を行う方法、Thymeleaf
を使用してHTML
テンプレートをレンダリングする方法、Spring Security
を使用してセキュリティを提供する方法を学びました。
総じて、Spring Boot
は非常に強力なフレームワークであり、その簡素化された設定と迅速な開発機能は、Web
開発分野で広く利用されています。開発者にとって、Spring Boot
を使用することで開発時間を短縮し、生産性を向上させることができ、同時にコードの品質も向上します。