基于springboot和layui实现的多法人社团管理系统

到山东找蓝翔

发布日期: 2021-09-07 14:25:40 浏览量: 147
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1.项目简介

1.1 目的和意义

现如今大学社团各式各样的都有,学校对社团的管理手段处于落后阶段,以致于对社团的未来发展以及社团的活动开展都有一定的影响,导致大学校内社团不能健康的发展,所以有必要建设一个大学社团管理系统来帮助学校管理校内社团,给学校提供一个管理社团更有效的一个手段,本课题设计的就是一个大学社团管理系统,本系统的开发就是为学校提供一个监管大学社团的平台,为社团提供一个在线收纳社员的平台,为学生提供一个在线选择社团的平台,为每个用户提供全面且贴心的服务功能。

随着高校的扩招,大学生群体不断壮大以及社团的多样化,学生社团这样一个学生组织也不断的壮大,高校社团文化日渐丰富,但当前我国高校学生社团发展中也存在不少问题,如“数量少”、“活动方式单一”、“资金短缺,物质条件较差”、“管理不规范”等一系列问题。如何更好地利用大学生社团的组织和活动,将其建设成为进行思想教育的有利阵地、学术探讨思想交流的第二课堂,是高校学生工作的一个重要课题。以及随之而来的繁琐的社团事务,使管理学生社团的工作变得不再那么容易,随着软件行业的发展,我们可以根据学生社团管理的需求来使用办公自动化来管理学生社团,介于社团事务的繁琐性,有必要开发这样一个系统来解决社团事务的繁琐性,提高办事效率。

1.2 国内外的研究现状

从上世纪五六十年代管理信息系统产生以来,管理信息系统理论发展得较为成熟。管理信息系统项目的实施对许多学校的发展起到了重要的作用。

随着计算机网络技术的迅速普及,现在在全国大部分高校都将学校日常管理活动纳入到管理信息系统中。但通国外高校信息技术起步早、起点高的特点相比,中国许多高校的管理信息系统徒具其形,却管理混乱。因此,国内高校在管理信息方面需要提高自身的软实力。

近年来,信息技术的突飞猛进,是的管理信息系统已经应用到大学管理中的各个领域当中,极大地提高了工作效率,因此开发一套完善的学生社团系统是十分必要的。对于社团管理的效率有很大的提高,并且可以节省人力、物力和财力,实现学校资源的最佳配置。

1.3 主要研究内容

系统整体架构

管理员后台进行社团管理的操作以及审核状态的操作,然后用户进入前台选择查看和加入社团,等待管理员审核,管理员接收到消息并处理来达到两者间的交互。

1.4 系统功能设计

本系统主要分为管理员和学生两种用户。

管理员功能如下:

  • 用户管理

  • 社团管理

  • 审核管理

  • 社联新闻管理

用户功能如下:

  • 注册、登录

  • 查看社团、加入社团

  • 创建社团

  • 查看社团活动

  • 申请社团活动

2.数据库设计

2.1 表结构

用户表

权限表

日志表

管理员表

![](/upload/image/41223/345ac4fbc3ddbdcba5621793119fbbee.png)

活动表

社团表

部门表

物资表

2.2 E-R图

3.系统详细设计

3.1 详细设计

3.1.1 管理员

  • 管理员用户管理:对管理员用户进行修改操作

  • 社团管理:对社团进行管理,如:修改社团人员等操作

  • 审核管理:用户申请创建社团时,管理员审核并修改审核结果操作

  • 社联新闻管理:对社团的新闻进行管理,如:修改新闻内容等等

3.1.2 用户

  • 用户管理:用户可以对自己的用户信息进行修改,如:修改密码等

  • 查看社团:用户可以查看社团目前成员,社团信息

  • 添加社团:用户选择社团进行加入,处于等待状态,待管理员审核后等待结果

  • 创建社团:对用户创建社团操作进行管理

  • 申请社团活动:对用户所申请社团活动的管理

3.2 研究途径及技术路线

主要特色

通过实地调查本学校学生社团的运行现状,对系统开发进行可行性分析和需求分析,并深入了解业务流程和数据流程,在此基础上得出初步的开发方案,继而进行系统设计。该系统采用面向对象的程序设计方法,该方法是一种基于结构分析的以数据为中心的程序设计方法,其主要思想是将数据及处理这些数据的操作都封装在一个叫做类的数据结构里。这种方法描述的现实世界模型贴切、合理,更符合人们认识世界的思维方法。

涉及主要技术

  • 后台框架:SSM SSM框架集由SpringBoot、MyBatis两个开源框架整合而成

  • 数据库技术:MySQL

  • 安全框架:shiro

  • 前台框架:主要采用Layui,html,css,JavaScript等技术

3.3 项目接口文档

3.3.1 社团事务

事务插入

  1. POST请求:
  2. {
  3. "oid":"1",
  4. "uid":"201641413117",
  5. "priority":0,
  6. "createTime":"",
  7. "status":1,
  8. "tips":"音乐社设计"
  9. }
  10. 返回:
  11. {
  12. "code": 0,
  13. "msg": "成功",
  14. "data": "事务插入成功"
  15. }

事务删除

  1. GET请求:http://localhost:8080/api/organAffair/deleteDepartmentById?id=2
  2. {
  3. "code": 0,
  4. "msg": "成功",
  5. "data": "事务删除成功"
  6. }
  7. {
  8. "code": -1,
  9. "msg": "事务删除失败",
  10. "data": null
  11. }

事务更新

  1. POST请求:http://localhost:8080/api/organAffair/updateOrganAffair
  2. {
  3. "id":1,
  4. "oid":"1",
  5. "uid":"201641413117",
  6. "priority":0,
  7. "createTime":"",
  8. "status":0,
  9. "tips":"音乐社设计"
  10. }
  11. {
  12. "code": 0,
  13. "msg": "成功",
  14. "data": "事务更新成功"
  15. }

事务获取

  1. GET请求:http://localhost:8080/api/organAffair/getOrganAffairList?oid=1&current=1&size=2
  2. 返回
  3. {
  4. "pages": 1,
  5. "current": 1,
  6. "total": 1,
  7. "data": [
  8. {
  9. "id": 1,
  10. "oid": 1,
  11. "uid": "201641413418",
  12. "priority": 0,
  13. "logname": "音乐社团测试",
  14. "createTime": "2019-09-18T15:58:16.000+0000",
  15. "status": 0,
  16. "tips": "音乐社设计"
  17. }
  18. ]
  19. }

3.3.2 社团团体

  1. 名字模糊查询
  2. GEThttp://localhost:8080/api/organ/queryOrganListByName?name=篮球
  3. 返回:
  4. {
  5. "pages":1,
  6. "current":1,
  7. "total":2,
  8. "data":[
  9. {
  10. "id":1,
  11. "name":"篮球社",
  12. "create_date":null,
  13. "nums":120,
  14. "level":3,
  15. "details":"篮球火"
  16. },
  17. {
  18. "id":2,
  19. "name":"篮球社",
  20. "create_date":null,
  21. "nums":0,
  22. "level":3,
  23. "details":"篮球火"
  24. }
  25. ]
  26. }
  27. GET http://localhost:8080/api/organ/queryOrganListById?id=1
  28. 返回:
  29. {
  30. "id":1,
  31. "name":"篮球社",
  32. "create_date":null,
  33. "nums":120,
  34. "level":3,
  35. "details":"篮球火"
  36. }
  37. 无条件获取列表GET http://localhost:8080/api/organ/queryOrganList
  38. {
  39. "pages":1,
  40. "current":1,
  41. "total":2,
  42. "data":[
  43. {
  44. "id":1,
  45. "name":"篮球社",
  46. "create_date":null,
  47. "nums":120,
  48. "level":3,
  49. "details":"篮球火"
  50. },
  51. {
  52. "id":2,
  53. "name":"篮球社",
  54. "create_date":null,
  55. "nums":0,
  56. "level":3,
  57. "details":"篮球火"
  58. }
  59. ]
  60. }
  61. 根据社团ID删除GET http://localhost:8080/api/organ/deleteOrgan?id=1
  62. 返回
  63. {
  64. "code":0,
  65. "msg":"成功",
  66. "data":"社团删除成功"
  67. }
  68. 更新社团 POST http://localhost:8080//api/organ/updateOrgan
  69. {
  70. "oid":2,
  71. "name":"篮球社",
  72. "create_date":"2019年八月"
  73. }
  74. 返回:
  75. {
  76. "code":0,
  77. "msg":"成功",
  78. "data":"社团更新成功"
  79. }
  80. 插入社团:POST http://localhost:8080//api/organ/insertOrgan
  81. {
  82. "oid":3,
  83. "name":"游泳社",
  84. "create_date":"2019年八月",
  85. "level":1,
  86. "details":"游泳"
  87. }
  88. 返回:
  89. {
  90. "code":0,
  91. "msg":"成功",
  92. "data":"社团插入成功"
  93. }

3.3.3 社团物资管理

  1. name模糊查询GET http://localhost:8080/api/goods/queryGoodsListByName?name=ball
  2. 返回:
  3. {
  4. "pages":1,
  5. "current":1,
  6. "total":2,
  7. "data":[
  8. {
  9. "id":1,
  10. "oid":1,
  11. "name":"money",
  12. "nums":"1",
  13. "price":"100",
  14. "tips":"test"
  15. },
  16. {
  17. "id":2,
  18. "oid":2,
  19. "name":"ball",
  20. "nums":"2",
  21. "price":"200",
  22. "tips":"test"
  23. }
  24. ]
  25. }
  26. 根据oid查询GET http://localhost:8080/api/goods/queryGoodsListByOid?oid=1
  27. 返回:
  28. {
  29. "id":1,
  30. "oid":1,
  31. "name":"money",
  32. "nums":"1",
  33. "price":"100",
  34. "tips":"test"
  35. }
  36. 无条件获取列表GET http://localhost:8080/api/goods/queryGoodsList
  37. 返回:
  38. {
  39. "pages":1,
  40. "current":1,
  41. "total":2,
  42. "data":[
  43. {
  44. "id":1,
  45. "oid":1,
  46. "name":"money",
  47. "nums":"1",
  48. "price":"100",
  49. "tips":"test"
  50. },
  51. {
  52. "id":2,
  53. "oid":2,
  54. "name":"ball",
  55. "nums":"2",
  56. "price":"200",
  57. "tips":"test"
  58. }
  59. ]
  60. }
  61. 根据社团物资ID删除GET http://localhost:8080/api/goods/deleteGoods?oid=2
  62. 返回:
  63. {
  64. "code":0,
  65. "msg":"成功",
  66. "data":"社团删除成功"
  67. }
  68. 更新社团:POST http://localhost:8080//api/goods/UpdateGoods
  69. {
  70. "oid":1,
  71. "name":"ball"
  72. }
  73. 返回:
  74. {
  75. "code":0,
  76. "msg":"成功",
  77. "data":"物资更新成功"
  78. }
  79. 插入社团:POST http://localhost:8080//api/goods/InsertGoods
  80. {
  81. "oid":"4",
  82. "name":"desk",
  83. "nums":"5",
  84. "price":5,
  85. "tips":"test",
  86. "id":"4"
  87. }
  88. 返回:
  89. {
  90. "code":0,
  91. "msg":"成功",
  92. "data":"物资插入成功"
  93. }

3.3.4 社团进行中活动查询

  1. current当前页size 每页条数默认10 GET http://localhost:8080/api/organ/activity/getOrganActivityListActive?&current=1&size=2
  2. {
  3. "code":0,
  4. "pages":1,
  5. "current":1,
  6. "total":1,
  7. "data":[
  8. {
  9. "id":1,
  10. "oid":1,
  11. "name":"音乐社",
  12. "title":"十大歌手",
  13. "details":"唱歌",
  14. "status":1
  15. }
  16. ]
  17. }
  18. GET http://localhost:8080/api/organ/affair/getOrganAffairListByUid?uid=201641413417&current=1&size=10
  19. {
  20. "code":0,
  21. "pages":1,
  22. "current":1,
  23. "total":3,
  24. "data":[
  25. {
  26. "id":1,
  27. "oid":1,
  28. "uid":"201641413417",
  29. "priority":1,
  30. "logname":"加入社团",
  31. "createTime":"2019-10-08 10:47:01",
  32. "status":1,
  33. "tips":"篮球社"
  34. },
  35. {
  36. "id":2,
  37. "oid":1,
  38. "uid":"201641413417",
  39. "priority":1,
  40. "logname":"参加活动",
  41. "createTime":"2019-10-08 15:29:04",
  42. "status":1,
  43. "tips":"篮球社"
  44. },
  45. {
  46. "id":3,
  47. "oid":1,
  48. "uid":"201641413417",
  49. "priority":1,
  50. "logname":"参加活动",
  51. "createTime":"2019-10-08 15:29:12",
  52. "status":1,
  53. "tips":"篮球社"
  54. }
  55. ]
  56. }

3.4 工具类

时间工具

  1. /**
  2. * 时间转换
  3. */
  4. public class DateTimeConvert {
  5. public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
  6. // str to date
  7. public static Date strToDate(String str, String pattern) {
  8. if (StringUtils.isBlank(str)) {
  9. return null;
  10. }
  11. DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
  12. DateTime dateTime = dateTimeFormatter.parseDateTime(str);
  13. return dateTime.toDate();
  14. }
  15. // common str to data
  16. public static Date strToDate(String str) {
  17. if (StringUtils.isBlank(str)) {
  18. return null;
  19. }
  20. DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
  21. DateTime dateTime = dateTimeFormatter.parseDateTime(str);
  22. return dateTime.toDate();
  23. }
  24. // date to str
  25. public static String dateToStr(Date date, String pattern) {
  26. if (date == null) {
  27. return StringUtils.EMPTY;
  28. }
  29. DateTime dateTime = new DateTime(date);
  30. return dateTime.toString(pattern);
  31. }
  32. // common date to str
  33. public static String dateToStr(Date date) {
  34. if (date == null) {
  35. return StringUtils.EMPTY;
  36. }
  37. DateTime dateTime = new DateTime(date);
  38. return dateTime.toString(STANDARD_FORMAT);
  39. }
  40. }

日志处理工具类

  1. /**
  2. * 日志处理工具类
  3. */
  4. public class LogUtil {
  5. public static Log getLog(String type, String operate, String result, HttpServletRequest request){
  6. Log log = new Log();
  7. Date date = new Date();
  8. String ip = IPUtil.getIpAddr(request);
  9. // 该属性在检查token的时候已经放入
  10. HttpSession session = request.getSession();
  11. String account = (String)session.getAttribute("account");
  12. // 若果登录失败则为null,设置为-1
  13. if(account == null){
  14. account = "非法用户";
  15. }
  16. log.setMessage(operate);
  17. log.setAccount(account);
  18. log.setCreateTime(date);
  19. log.setIp(ip);
  20. log.setLogname(type);
  21. log.setSucceed(result);
  22. return log;
  23. }
  24. }

拦截器配置

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception
  3. {
  4. http.headers().frameOptions().sameOrigin();
  5. http.authorizeRequests()
  6. .antMatchers("/api/admin/**").hasAnyRole("admin","student")
  7. .antMatchers("/api/organ/**").hasAnyRole("student","admin")
  8. .antMatchers("/Student/**").hasRole("student")
  9. .antMatchers("/api/student/**").hasRole("student")
  10. .antMatchers("/layui/**").permitAll()
  11. .antMatchers("/WeAdmin/**").permitAll()
  12. .antMatchers("/Student/**").permitAll()
  13. .anyRequest().authenticated()
  14. .and()
  15. .formLogin()
  16. .loginPage("/WeAdmin/login.html")
  17. .loginProcessingUrl("/login").permitAll()
  18. .usernameParameter("username")
  19. .passwordParameter("passwd")
  20. .successHandler(new AuthenticationSuccessHandler() {
  21. @Override
  22. public void onAuthenticationSuccess(HttpServletRequest Request, HttpServletResponse Response, Authentication authentication) throws IOException, ServletException {
  23. HttpSession session = Request.getSession();
  24. Account account = (Account) SecurityContextHolder.getContext()
  25. .getAuthentication()
  26. .getPrincipal();
  27. System.out.println(account.getPassword());
  28. session.setAttribute("account",account.getUsername());
  29. logService.insertLog(LogUtil.getLog("登录日志","登陆成功","操作成功",Request));
  30. Object principal = authentication.getPrincipal();
  31. if(account.getRoleid() == 1)
  32. Response.sendRedirect("/WeAdmin/index.html");
  33. else
  34. Response.sendRedirect("/Student/navbar.html");
  35. }
  36. })
  37. .failureHandler(new AuthenticationFailureHandler() {
  38. @Override
  39. public void onAuthenticationFailure(HttpServletRequest Request, HttpServletResponse Response, AuthenticationException e) throws IOException, ServletException {
  40. Response.setContentType("application/json;charset=utf-8");
  41. PrintWriter out = Response.getWriter();
  42. Response.setStatus(401);
  43. Map<String, Object> map = new HashMap<>();
  44. ObjectMapper om = new ObjectMapper();
  45. map.put("status",401);
  46. if(e instanceof BadCredentialsException){
  47. map.put("msg","账号或密码错误");
  48. }else{
  49. map.put("msg","登陆失败");
  50. }
  51. out.write(om.writeValueAsString(map));
  52. out.flush();
  53. out.close();
  54. }
  55. })
  56. .and()
  57. .logout()
  58. .logoutUrl("/logout")
  59. .clearAuthentication(true)
  60. .invalidateHttpSession(true)
  61. .addLogoutHandler(new LogoutHandler() {
  62. @Override
  63. public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
  64. }
  65. })
  66. .logoutSuccessHandler(new LogoutSuccessHandler() {
  67. @Override
  68. public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
  69. httpServletResponse.sendRedirect("/login.html");
  70. }
  71. })
  72. .permitAll()
  73. .and()
  74. .csrf().disable();
  75. }
  76. @Bean
  77. PasswordEncoder passwordEncoder(){
  78. return new BCryptPasswordEncoder();
  79. }
  80. @Override
  81. protected void configure(AuthenticationManagerBuilder auth)throws Exception{
  82. auth.userDetailsService(accountService);
  83. }
  84. /** 放行静态资源 */
  85. @Override
  86. public void configure(WebSecurity web) throws Exception {
  87. //解决静态资源被拦截的问题
  88. web.ignoring().antMatchers("/css/**");
  89. web.ignoring().antMatchers("/page/**");
  90. web.ignoring().antMatchers("/js/**");
  91. web.ignoring().antMatchers("/layui/**");
  92. web.ignoring().antMatchers("/images/**");
  93. web.ignoring().antMatchers("/WeAdmin/**");
  94. }
  95. }

3.5 业务代码

  1. @Override
  2. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  3. //获取账户信息
  4. QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
  5. wrapper.eq("account",s);
  6. Account account = accountMapper.selectOne(wrapper);
  7. System.out.println(account.getPassword());
  8. if(account == null){
  9. throw new UsernameNotFoundException("账户不存在");
  10. }
  11. //获取角色ID
  12. int id = account.getRoleid();
  13. QueryWrapper<Role> rwrapper = new QueryWrapper<Role>();
  14. rwrapper.eq("id",id);
  15. //设置角色
  16. account.setRole(roleMapper.selectOne(rwrapper));
  17. return account;
  18. }
  19. //获取管理员信息
  20. public ResponsePage<Account> queryAccount(String s){
  21. QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
  22. System.out.println("传入名字:"+s);
  23. wrapper.eq("account",s);
  24. Account account = accountMapper.selectOne(wrapper);
  25. if (account == null){
  26. ResponsePage<Account> responsePage= new ResponsePage<Account>();
  27. responsePage.setTotal(10);
  28. responsePage.setCode(0);
  29. responsePage.setCurrent(1);
  30. List<Account> l = new LinkedList<Account>();
  31. responsePage.setData(l);
  32. return responsePage;
  33. }
  34. //获取角色ID
  35. int id = account.getRoleid();
  36. QueryWrapper<Role> rwrapper = new QueryWrapper<Role>();
  37. rwrapper.eq("id",id);
  38. //设置角色
  39. account.setRole(roleMapper.selectOne(rwrapper));
  40. ResponsePage<Account> responsePage= new ResponsePage<Account>();
  41. responsePage.setTotal(10);
  42. responsePage.setCode(0);
  43. responsePage.setCurrent(1);
  44. List<Account> l = new LinkedList<Account>();
  45. l.add(account);
  46. responsePage.setData(l);
  47. return responsePage;
  48. }
  49. /**
  50. * ID获取管理员信息
  51. * @param id
  52. * @return
  53. */
  54. public Account queryAccountById(int id){
  55. return accountMapper.selectById(id);
  56. }
  57. /**
  58. * 获取管理员列表
  59. * @param current
  60. * @param size
  61. * @return
  62. */
  63. public ResponsePage<Account> queryAccountList(int current, int size){
  64. Page<Account> page = new Page<Account>(current, size);
  65. IPage<Account> iPage = accountMapper.selectPage(page,null);
  66. ResponsePage<Account> responsePage= new ResponsePage<Account>();
  67. responsePage.makePage(iPage);
  68. return responsePage;
  69. }
  70. /**
  71. * 插入管理员
  72. * @param account
  73. * @return
  74. */
  75. public int insertAccount(Account account){
  76. account.setCreateTime(new Date());
  77. account.setModifyTime(new Date());
  78. int result = accountMapper.insert(account);
  79. return result;
  80. }
  81. /**
  82. * 删除管理员
  83. * @param id
  84. * @return
  85. */
  86. public int deleteAccount(int id){
  87. int result = accountMapper.deleteById(id);
  88. return result;
  89. }
  90. /**
  91. * 更新管理员
  92. * @param account
  93. * @return
  94. */
  95. public int updateAccount(Account account){
  96. QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
  97. wrapper.eq("id",account.getId());
  98. account.setModifyTime(new Date());
  99. int result = accountMapper.update(account,wrapper);
  100. return result;
  101. }
  102. /**
  103. * 重置密码
  104. * @param oldpass
  105. * @param newpass
  106. * @param account
  107. */
  108. public Boolean resetPasswd(String oldpass,String newpass,String account){
  109. QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
  110. wrapper.eq("account",account);
  111. Account account1 = accountMapper.selectOne(wrapper);
  112. PasswordEncoder password = new BCryptPasswordEncoder();
  113. if( password.matches(oldpass, account1.getPassword())) {
  114. account1.setPassword(password.encode(newpass));
  115. accountMapper.updateById(account1);
  116. return true;
  117. }else {
  118. return false;
  119. }
  120. }

4.项目展示

4.1 普通用户

首页

活动列表

模糊查询

社团列表

个人信息

4.2 教师用户

4.3 管理员用户

首页

社团管理

成员管理

活动管理

事务管理

导出功能

物资管理

用户管理

日志管理

5.主要参考文献。

[1] 萨师煊,王珊编著.数据库系统概论(第四版).北京:高等教育出版社.,ISBN 978-7-04-019583-5

[2] 周洋,何丽丽.基于B/S模式的高校社团管理系统的研究与设计[J].电脑知识与技术,2018,14(33):84-85+96.

[3] 韩璐. 高校学生社团管理系统的设计与实现[D].电子科技大学,2019.

[4] 龚文辉. 高校大学生社团管理平台的设计与实现[D].江西师范大学,2018.

[5] 郝平.学生社团管理系统设计与实现研究[J].电脑迷,2018(02):26.

[6] 石志国刘翼伟王志良编著 JSP应用教程(修订本),清华大学出版社,北京交通大学出版社2008.6

[7] 袁鹏飞,孙君安.中文版SQL Server2005数据库系统管理.北京:人民邮电出版社,2001

[8] 郑阿奇主编,刘启芬,顾韵华.SQL Server实用教程(第2版).北京:电子工业出版社. 7,202-203

[9] 王军牛志玲译 SQL Server 2012编程入门经典(第4版)清华大学出版社 2013,4

上传的附件 cloud_download 设计文档.docx ( 1.85mb, 3次下载 ) cloud_download community_system-master.zip ( 9.79mb, 3次下载 )
error_outline 下载需要12点积分

发送私信

会冒泡的可乐

111
文章数
12
评论数
最近文章
eject