.cursorrules 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. # 杭州第一技师学院智慧校园管理系统 - AI 代码生成规范
  2. ## 🎯 项目概述
  3. 本项目是基于 RuoYi-Vue-Plus 框架开发的智慧校园管理系统,主要用于学校的学工管理、宿舍管理、班级管理等业务场景。
  4. - **项目名称**: 杭州第一技师学院智慧校园管理系统
  5. - **基础框架**: RuoYi-Vue-Plus 5.4.0
  6. - **前端**: Vue 3 + TypeScript + Element Plus + Vite
  7. - **后端**: Spring Boot 3.4.6 + MyBatis-Plus + Sa-Token
  8. ---
  9. ## ⚠️ AI代码生成核心规则
  10. ### 文档生成规范
  11. - **禁止主动生成MD文档**:在代码生成过程中,除非用户明确要求,否则不要生成任何Markdown文档(.md文件)
  12. - **简洁优先原则**:如果必须生成说明文档,内容要极度简洁,只包含关键信息
  13. - **聚焦代码实现**:将精力集中在代码生成和功能实现上,而非文档编写
  14. - **例外情况**:仅在以下情况生成文档
  15. - 用户明确要求生成文档
  16. - 项目README或API文档等必要文件缺失
  17. - 复杂功能需要关键说明(限制在50行以内)
  18. ---
  19. ## 📦 前端技术栈总结
  20. ### 核心框架与版本
  21. - **Vue**: 3.5.13 (使用 Composition API)
  22. - **TypeScript**: 5.8.3
  23. - **Vite**: 6.3.2
  24. - **Element Plus**: 2.9.8
  25. - **Pinia**: 3.0.2 (状态管理)
  26. - **Vue Router**: 4.5.0 (路由管理)
  27. - **Axios**: 1.8.4 (HTTP请求)
  28. ### 关键工具库
  29. - **vxe-table**: 4.13.7 (高级表格组件)
  30. - **UnoCSS**: 66.0.0 (原子化CSS框架)
  31. - **echarts**: 5.6.0 (数据可视化)
  32. - **@vueuse/core**: 13.1.0 (Vue组合式工具集)
  33. - **vue-i18n**: 11.1.3 (国际化)
  34. - **screenfull**: 6.0.2 (全屏)
  35. - **nprogress**: 0.2.0 (进度条)
  36. ### 项目结构
  37. ```
  38. web/
  39. ├── src/
  40. │ ├── api/ # API接口定义
  41. │ ├── assets/ # 静态资源
  42. │ ├── components/ # 公共组件
  43. │ ├── directive/ # 自定义指令
  44. │ ├── hooks/ # 组合式函数
  45. │ ├── lang/ # 国际化语言包
  46. │ ├── layout/ # 布局组件
  47. │ ├── plugins/ # 插件配置
  48. │ ├── router/ # 路由配置
  49. │ ├── store/ # Pinia状态管理
  50. │ ├── utils/ # 工具函数
  51. │ ├── views/ # 页面视图
  52. │ └── main.ts # 入口文件
  53. ├── vite/ # Vite插件配置
  54. ├── index.html
  55. ├── vite.config.ts
  56. └── package.json
  57. ```
  58. ---
  59. ## 📦 后端技术栈总结
  60. ### 核心框架与版本
  61. - **Spring Boot**: 3.4.6
  62. - **JDK**: 17+
  63. - **MyBatis**: 3.5.16
  64. - **MyBatis-Plus**: 3.5.12
  65. - **Sa-Token**: 1.42.0 (权限认证)
  66. - **Hutool**: 5.8.35 (工具库)
  67. ### 关键技术组件
  68. - **Redisson**: 3.45.1 (Redis客户端,分布式锁)
  69. - **Lock4j**: 2.2.7 (分布式锁)
  70. - **dynamic-datasource**: 4.3.1 (动态多数据源)
  71. - **p6spy**: 3.9.1 (SQL性能分析)
  72. - **FastExcel**: 1.2.0 (Excel导入导出)
  73. - **SpringDoc**: 2.8.8 (API文档生成)
  74. - **Warm-Flow**: 1.7.3 (国产工作流引擎)
  75. - **SnailJob**: 1.5.0 (分布式任务调度)
  76. - **AWS S3**: 2.28.22 (对象存储)
  77. - **SMS4J**: 3.3.4 (短信发送)
  78. - **ip2region**: 2.7.0 (IP地址定位)
  79. - **MapStruct-Plus**: 1.5.0 (对象转换)
  80. ### 项目结构
  81. ```
  82. backend/
  83. ├── ruoyi-admin/ # 启动模块
  84. ├── ruoyi-common/ # 通用模块
  85. │ ├── ruoyi-common-core/ # 核心工具
  86. │ ├── ruoyi-common-mybatis/ # 数据库配置
  87. │ ├── ruoyi-common-redis/ # Redis配置
  88. │ ├── ruoyi-common-satoken/ # 权限配置
  89. │ ├── ruoyi-common-web/ # Web配置
  90. │ ├── ruoyi-common-excel/ # Excel工具
  91. │ └── ... # 其他通用模块
  92. ├── ruoyi-modules/ # 业务模块
  93. │ ├── ruoyi-system/ # 系统管理
  94. │ ├── ruoyi-job/ # 定时任务
  95. │ ├── ruoyi-workflow/ # 工作流
  96. │ └── ruoyi-xg/ # 学工管理 ⭐
  97. └── ruoyi-extend/ # 扩展模块
  98. ├── ruoyi-monitor-admin/ # 监控中心
  99. └── ruoyi-snailjob-server/# 任务调度服务
  100. ```
  101. ---
  102. ## 🎓 学工模块(ruoyi-xg)架构规范
  103. ### 后端模块结构
  104. ```
  105. ruoyi-xg/
  106. └── src/main/java/org/dromara/xg/
  107. ├── controller/ # 控制器层 - RESTful API
  108. ├── service/ # 服务层 - 业务逻辑
  109. │ └── impl/ # 服务实现
  110. ├── mapper/ # 数据访问层 - MyBatis接口
  111. ├── domain/ # 数据模型
  112. │ ├── bo/ # Business Object - 业务对象(接收参数)
  113. │ └── vo/ # View Object - 视图对象(返回数据)
  114. ├── convert/ # 对象转换器 - MapStruct
  115. ├── config/ # 配置类
  116. ├── utils/ # 工具类
  117. └── listener/ # 事件监听器
  118. ```
  119. ### 前端模块结构
  120. ```
  121. web/src/
  122. ├── api/xg/ # 学工API接口定义
  123. │ ├── student/ # 学生管理API
  124. │ ├── xsjcxx/ # 学生基础信息API
  125. │ ├── xgss/ # 学工宿舍API
  126. │ ├── studentStatistics.ts # 学生统计API
  127. │ └── README.md
  128. └── views/xg/ # 学工页面视图
  129. ├── xsjcxx/ # 学生基础信息模块
  130. ├── xgss/ # 学工宿舍模块
  131. ├── studentStatistics/ # 学生统计模块
  132. └── README.md
  133. ```
  134. ---
  135. ## 🚀 后端开发规范(学工模块)
  136. ### 1. Controller 层规范
  137. ```java
  138. package org.dromara.xg.controller;
  139. import cn.dev33.satoken.annotation.SaCheckPermission;
  140. import jakarta.servlet.http.HttpServletResponse;
  141. import jakarta.validation.constraints.*;
  142. import lombok.RequiredArgsConstructor;
  143. import org.dromara.common.core.domain.R;
  144. import org.dromara.common.core.validate.AddGroup;
  145. import org.dromara.common.core.validate.EditGroup;
  146. import org.dromara.common.excel.utils.ExcelUtil;
  147. import org.dromara.common.idempotent.annotation.RepeatSubmit;
  148. import org.dromara.common.log.annotation.Log;
  149. import org.dromara.common.log.enums.BusinessType;
  150. import org.dromara.common.mybatis.core.page.*;
  151. import org.dromara.common.web.core.BaseController;
  152. import org.springframework.validation.annotation.Validated;
  153. import org.springframework.web.bind.annotation.*;
  154. /**
  155. * XXX管理控制器
  156. *
  157. * @author Your Name
  158. * @date 2025-01-XX
  159. */
  160. @Validated
  161. @RequiredArgsConstructor
  162. @RestController
  163. @RequestMapping("/xg/模块名")
  164. public class XgXxxController extends BaseController {
  165. private final IXgXxxService xxxService;
  166. /**
  167. * 查询列表(分页)
  168. */
  169. @SaCheckPermission("xg:xxx:list")
  170. @GetMapping("/list")
  171. public TableDataInfo<XgXxxVo> list(XgXxxBo bo, PageQuery pageQuery) {
  172. return xxxService.queryPageList(bo, pageQuery);
  173. }
  174. /**
  175. * 获取详细信息
  176. */
  177. @SaCheckPermission("xg:xxx:query")
  178. @GetMapping("/{id}")
  179. public R<XgXxxVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
  180. return R.ok(xxxService.queryById(id));
  181. }
  182. /**
  183. * 新增
  184. */
  185. @SaCheckPermission("xg:xxx:add")
  186. @Log(title = "XXX管理", businessType = BusinessType.INSERT)
  187. @RepeatSubmit()
  188. @PostMapping()
  189. public R<Void> add(@Validated(AddGroup.class) @RequestBody XgXxxBo bo) {
  190. return toAjax(xxxService.insertByBo(bo));
  191. }
  192. /**
  193. * 修改
  194. */
  195. @SaCheckPermission("xg:xxx:edit")
  196. @Log(title = "XXX管理", businessType = BusinessType.UPDATE)
  197. @RepeatSubmit()
  198. @PutMapping()
  199. public R<Void> edit(@Validated(EditGroup.class) @RequestBody XgXxxBo bo) {
  200. return toAjax(xxxService.updateByBo(bo));
  201. }
  202. /**
  203. * 删除
  204. */
  205. @SaCheckPermission("xg:xxx:remove")
  206. @Log(title = "XXX管理", businessType = BusinessType.DELETE)
  207. @DeleteMapping("/{ids}")
  208. public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
  209. return toAjax(xxxService.deleteWithValidByIds(List.of(ids), true));
  210. }
  211. /**
  212. * 导出
  213. */
  214. @SaCheckPermission("xg:xxx:export")
  215. @Log(title = "XXX管理", businessType = BusinessType.EXPORT)
  216. @PostMapping("/export")
  217. public void export(XgXxxBo bo, HttpServletResponse response) {
  218. List<XgXxxVo> list = xxxService.queryList(bo);
  219. ExcelUtil.exportExcel(list, "XXX数据", XgXxxVo.class, response);
  220. }
  221. }
  222. ```
  223. **关键点:**
  224. - 继承 `BaseController` 提供基础工具方法
  225. - 使用 `@RequiredArgsConstructor` 实现构造器注入
  226. - 使用 `@SaCheckPermission` 进行权限控制,格式:`xg:模块:操作`
  227. - 使用 `@Log` 记录操作日志
  228. - 使用 `@RepeatSubmit` 防止重复提交
  229. - 使用 `@Validated` 进行参数校验,分组校验使用 AddGroup/EditGroup
  230. - 分页查询返回 `TableDataInfo<T>`,其他接口返回 `R<T>`
  231. ### 2. Service 层规范
  232. ```java
  233. package org.dromara.xg.service;
  234. import org.dromara.common.mybatis.core.page.*;
  235. import org.dromara.xg.domain.bo.XgXxxBo;
  236. import org.dromara.xg.domain.vo.XgXxxVo;
  237. import java.util.Collection;
  238. import java.util.List;
  239. /**
  240. * XXX管理Service接口
  241. *
  242. * @author Your Name
  243. * @date 2025-01-XX
  244. */
  245. public interface IXgXxxService {
  246. /**
  247. * 查询分页列表
  248. */
  249. TableDataInfo<XgXxxVo> queryPageList(XgXxxBo bo, PageQuery pageQuery);
  250. /**
  251. * 查询列表
  252. */
  253. List<XgXxxVo> queryList(XgXxxBo bo);
  254. /**
  255. * 根据主键查询
  256. */
  257. XgXxxVo queryById(Long id);
  258. /**
  259. * 新增
  260. */
  261. Boolean insertByBo(XgXxxBo bo);
  262. /**
  263. * 修改
  264. */
  265. Boolean updateByBo(XgXxxBo bo);
  266. /**
  267. * 校验并批量删除
  268. */
  269. Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
  270. }
  271. ```
  272. **实现类规范:**
  273. ```java
  274. package org.dromara.xg.service.impl;
  275. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  276. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  277. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  278. import lombok.RequiredArgsConstructor;
  279. import org.dromara.common.core.utils.MapstructUtils;
  280. import org.dromara.common.core.utils.StringUtils;
  281. import org.dromara.common.mybatis.core.page.*;
  282. import org.dromara.xg.domain.XgXxx;
  283. import org.dromara.xg.domain.bo.XgXxxBo;
  284. import org.dromara.xg.domain.vo.XgXxxVo;
  285. import org.dromara.xg.mapper.XgXxxMapper;
  286. import org.dromara.xg.service.IXgXxxService;
  287. import org.springframework.stereotype.Service;
  288. import java.util.Collection;
  289. import java.util.List;
  290. /**
  291. * XXX管理Service实现
  292. *
  293. * @author Your Name
  294. * @date 2025-01-XX
  295. */
  296. @RequiredArgsConstructor
  297. @Service
  298. public class XgXxxServiceImpl implements IXgXxxService {
  299. private final XgXxxMapper baseMapper;
  300. /**
  301. * 构建查询条件
  302. */
  303. private LambdaQueryWrapper<XgXxx> buildQueryWrapper(XgXxxBo bo) {
  304. LambdaQueryWrapper<XgXxx> lqw = Wrappers.lambdaQueryWrapper();
  305. lqw.like(StringUtils.isNotBlank(bo.getName()), XgXxx::getName, bo.getName());
  306. lqw.eq(bo.getStatus() != null, XgXxx::getStatus, bo.getStatus());
  307. lqw.between(bo.getBeginTime() != null && bo.getEndTime() != null,
  308. XgXxx::getCreateTime, bo.getBeginTime(), bo.getEndTime());
  309. lqw.orderByDesc(XgXxx::getCreateTime);
  310. return lqw;
  311. }
  312. @Override
  313. public TableDataInfo<XgXxxVo> queryPageList(XgXxxBo bo, PageQuery pageQuery) {
  314. LambdaQueryWrapper<XgXxx> lqw = buildQueryWrapper(bo);
  315. Page<XgXxxVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
  316. return TableDataInfo.build(result);
  317. }
  318. @Override
  319. public List<XgXxxVo> queryList(XgXxxBo bo) {
  320. LambdaQueryWrapper<XgXxx> lqw = buildQueryWrapper(bo);
  321. return baseMapper.selectVoList(lqw);
  322. }
  323. @Override
  324. public XgXxxVo queryById(Long id) {
  325. return baseMapper.selectVoById(id);
  326. }
  327. @Override
  328. public Boolean insertByBo(XgXxxBo bo) {
  329. XgXxx entity = MapstructUtils.convert(bo, XgXxx.class);
  330. boolean flag = baseMapper.insert(entity) > 0;
  331. if (flag) {
  332. bo.setId(entity.getId());
  333. }
  334. return flag;
  335. }
  336. @Override
  337. public Boolean updateByBo(XgXxxBo bo) {
  338. XgXxx entity = MapstructUtils.convert(bo, XgXxx.class);
  339. return baseMapper.updateById(entity) > 0;
  340. }
  341. @Override
  342. public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
  343. if (isValid) {
  344. // 做删除前的业务校验
  345. }
  346. return baseMapper.deleteByIds(ids) > 0;
  347. }
  348. }
  349. ```
  350. **关键点:**
  351. - 使用 `@RequiredArgsConstructor` 注入 Mapper
  352. - 使用 MyBatis-Plus 的 LambdaQueryWrapper 构建查询条件
  353. - 使用 `MapstructUtils.convert` 进行对象转换
  354. - 使用 `selectVoPage` 和 `selectVoList` 直接查询VO对象
  355. ### 3. Domain 层规范
  356. **Entity(实体类):**
  357. ```java
  358. package org.dromara.xg.domain;
  359. import com.baomidou.mybatisplus.annotation.*;
  360. import lombok.Data;
  361. import lombok.EqualsAndHashCode;
  362. import org.dromara.common.tenant.core.TenantEntity;
  363. import java.io.Serial;
  364. import java.time.LocalDateTime;
  365. /**
  366. * XXX实体对象 xg_xxx
  367. *
  368. * @author Your Name
  369. * @date 2025-01-XX
  370. */
  371. @Data
  372. @EqualsAndHashCode(callSuper = true)
  373. @TableName("xg_xxx")
  374. public class XgXxx extends TenantEntity {
  375. @Serial
  376. private static final long serialVersionUID = 1L;
  377. /**
  378. * 主键ID
  379. */
  380. @TableId(value = "id", type = IdType.ASSIGN_ID)
  381. private Long id;
  382. /**
  383. * 名称
  384. */
  385. private String name;
  386. /**
  387. * 状态
  388. */
  389. private String status;
  390. /**
  391. * 备注
  392. */
  393. private String remark;
  394. /**
  395. * 逻辑删除标志(0存在 1删除)
  396. */
  397. @TableLogic
  398. private Integer delFlag;
  399. }
  400. ```
  401. **BO(业务对象 - 接收前端参数):**
  402. ```java
  403. package org.dromara.xg.domain.bo;
  404. import io.github.linpeilie.annotations.AutoMapper;
  405. import jakarta.validation.constraints.*;
  406. import lombok.Data;
  407. import lombok.EqualsAndHashCode;
  408. import org.dromara.common.core.validate.AddGroup;
  409. import org.dromara.common.core.validate.EditGroup;
  410. import org.dromara.common.mybatis.core.domain.BaseEntity;
  411. import org.dromara.xg.domain.XgXxx;
  412. /**
  413. * XXX业务对象 xg_xxx
  414. *
  415. * @author Your Name
  416. * @date 2025-01-XX
  417. */
  418. @Data
  419. @EqualsAndHashCode(callSuper = true)
  420. @AutoMapper(target = XgXxx.class, reverseConvertGenerate = false)
  421. public class XgXxxBo extends BaseEntity {
  422. /**
  423. * 主键ID
  424. */
  425. @NotNull(message = "主键不能为空", groups = { EditGroup.class })
  426. private Long id;
  427. /**
  428. * 名称
  429. */
  430. @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class })
  431. private String name;
  432. /**
  433. * 状态
  434. */
  435. private String status;
  436. /**
  437. * 备注
  438. */
  439. private String remark;
  440. }
  441. ```
  442. **VO(视图对象 - 返回给前端):**
  443. ```java
  444. package org.dromara.xg.domain.vo;
  445. import com.alibaba.excel.annotation.ExcelProperty;
  446. import io.github.linpeilie.annotations.AutoMapper;
  447. import lombok.Data;
  448. import org.dromara.common.excel.annotation.ExcelDictFormat;
  449. import org.dromara.common.excel.convert.ExcelDictConvert;
  450. import org.dromara.xg.domain.XgXxx;
  451. import java.io.Serial;
  452. import java.io.Serializable;
  453. import java.time.LocalDateTime;
  454. /**
  455. * XXX视图对象 xg_xxx
  456. *
  457. * @author Your Name
  458. * @date 2025-01-XX
  459. */
  460. @Data
  461. @AutoMapper(target = XgXxx.class)
  462. public class XgXxxVo implements Serializable {
  463. @Serial
  464. private static final long serialVersionUID = 1L;
  465. /**
  466. * 主键ID
  467. */
  468. @ExcelProperty(value = "ID")
  469. private Long id;
  470. /**
  471. * 名称
  472. */
  473. @ExcelProperty(value = "名称")
  474. private String name;
  475. /**
  476. * 状态
  477. */
  478. @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
  479. @ExcelDictFormat(dictType = "sys_normal_disable")
  480. private String status;
  481. /**
  482. * 备注
  483. */
  484. @ExcelProperty(value = "备注")
  485. private String remark;
  486. /**
  487. * 创建时间
  488. */
  489. @ExcelProperty(value = "创建时间")
  490. private LocalDateTime createTime;
  491. }
  492. ```
  493. ### 4. Mapper 层规范
  494. ```java
  495. package org.dromara.xg.mapper;
  496. import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  497. import org.dromara.xg.domain.XgXxx;
  498. import org.dromara.xg.domain.vo.XgXxxVo;
  499. /**
  500. * XXX管理Mapper接口
  501. *
  502. * @author Your Name
  503. * @date 2025-01-XX
  504. */
  505. public interface XgXxxMapper extends BaseMapperPlus<XgXxx, XgXxxVo> {
  506. // 自定义SQL方法在这里定义
  507. }
  508. ```
  509. **对应的 XML(如需自定义SQL):**
  510. ```xml
  511. <?xml version="1.0" encoding="UTF-8" ?>
  512. <!DOCTYPE mapper
  513. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  514. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  515. <mapper namespace="org.dromara.xg.mapper.XgXxxMapper">
  516. <resultMap type="org.dromara.xg.domain.vo.XgXxxVo" id="XgXxxResult">
  517. <result property="id" column="id"/>
  518. <result property="name" column="name"/>
  519. <result property="status" column="status"/>
  520. <result property="remark" column="remark"/>
  521. <result property="createTime" column="create_time"/>
  522. </resultMap>
  523. </mapper>
  524. ```
  525. ---
  526. ## 🎨 前端开发规范(学工模块)
  527. ### 1. API 接口定义规范
  528. ```typescript
  529. // web/src/api/xg/xxx.ts
  530. import request from '@/utils/request'
  531. /**
  532. * 查询参数接口
  533. */
  534. export interface XxxQuery {
  535. pageNum?: number
  536. pageSize?: number
  537. name?: string
  538. status?: string
  539. beginTime?: string
  540. endTime?: string
  541. }
  542. /**
  543. * 表单数据接口
  544. */
  545. export interface XxxForm {
  546. id?: number
  547. name: string
  548. status: string
  549. remark?: string
  550. }
  551. /**
  552. * 视图对象接口
  553. */
  554. export interface XxxVO {
  555. id: number
  556. name: string
  557. status: string
  558. remark?: string
  559. createTime: string
  560. updateTime: string
  561. }
  562. /**
  563. * 查询列表
  564. */
  565. export function listXxx(query: XxxQuery) {
  566. return request({
  567. url: '/xg/xxx/list',
  568. method: 'get',
  569. params: query
  570. })
  571. }
  572. /**
  573. * 查询详细
  574. */
  575. export function getXxx(id: number) {
  576. return request({
  577. url: `/xg/xxx/${id}`,
  578. method: 'get'
  579. })
  580. }
  581. /**
  582. * 新增
  583. */
  584. export function addXxx(data: XxxForm) {
  585. return request({
  586. url: '/xg/xxx',
  587. method: 'post',
  588. data: data
  589. })
  590. }
  591. /**
  592. * 修改
  593. */
  594. export function updateXxx(data: XxxForm) {
  595. return request({
  596. url: '/xg/xxx',
  597. method: 'put',
  598. data: data
  599. })
  600. }
  601. /**
  602. * 删除
  603. */
  604. export function delXxx(ids: number | number[]) {
  605. return request({
  606. url: `/xg/xxx/${ids}`,
  607. method: 'delete'
  608. })
  609. }
  610. /**
  611. * 导出
  612. */
  613. export function exportXxx(query: XxxQuery) {
  614. return request({
  615. url: '/xg/xxx/export',
  616. method: 'post',
  617. data: query,
  618. responseType: 'blob'
  619. })
  620. }
  621. ```
  622. **关键点:**
  623. - 接口定义使用 TypeScript interface
  624. - Query 参数用于列表查询(GET请求的params)
  625. - Form 用于新增/修改(POST/PUT请求的data)
  626. - VO 用于展示数据
  627. - 导出接口需要设置 `responseType: 'blob'`
  628. ### 2. 列表页面规范
  629. ```vue
  630. <!-- web/src/views/xg/xxx/index.vue -->
  631. <template>
  632. <div class="app-container">
  633. <!-- 查询表单 -->
  634. <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="80px">
  635. <el-form-item label="名称" prop="name">
  636. <el-input
  637. v-model="queryParams.name"
  638. placeholder="请输入名称"
  639. clearable
  640. @keyup.enter="handleQuery"
  641. />
  642. </el-form-item>
  643. <el-form-item label="状态" prop="status">
  644. <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
  645. <el-option
  646. v-for="dict in sys_normal_disable"
  647. :key="dict.value"
  648. :label="dict.label"
  649. :value="dict.value"
  650. />
  651. </el-select>
  652. </el-form-item>
  653. <el-form-item>
  654. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  655. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  656. </el-form-item>
  657. </el-form>
  658. <!-- 工具栏 -->
  659. <el-row :gutter="10" class="mb8">
  660. <el-col :span="1.5">
  661. <el-button
  662. type="primary"
  663. plain
  664. icon="Plus"
  665. @click="handleAdd"
  666. v-hasPermi="['xg:xxx:add']"
  667. >新增</el-button>
  668. </el-col>
  669. <el-col :span="1.5">
  670. <el-button
  671. type="success"
  672. plain
  673. icon="Edit"
  674. :disabled="single"
  675. @click="handleUpdate()"
  676. v-hasPermi="['xg:xxx:edit']"
  677. >修改</el-button>
  678. </el-col>
  679. <el-col :span="1.5">
  680. <el-button
  681. type="danger"
  682. plain
  683. icon="Delete"
  684. :disabled="multiple"
  685. @click="handleDelete()"
  686. v-hasPermi="['xg:xxx:remove']"
  687. >删除</el-button>
  688. </el-col>
  689. <el-col :span="1.5">
  690. <el-button
  691. type="warning"
  692. plain
  693. icon="Download"
  694. @click="handleExport"
  695. v-hasPermi="['xg:xxx:export']"
  696. >导出</el-button>
  697. </el-col>
  698. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
  699. </el-row>
  700. <!-- 数据表格 -->
  701. <el-table
  702. v-loading="loading"
  703. :data="xxxList"
  704. @selection-change="handleSelectionChange"
  705. >
  706. <el-table-column type="selection" width="55" align="center" />
  707. <el-table-column label="ID" align="center" prop="id" />
  708. <el-table-column label="名称" align="center" prop="name" :show-overflow-tooltip="true" />
  709. <el-table-column label="状态" align="center" prop="status">
  710. <template #default="scope">
  711. <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
  712. </template>
  713. </el-table-column>
  714. <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  715. <template #default="scope">
  716. <span>{{ parseTime(scope.row.createTime) }}</span>
  717. </template>
  718. </el-table-column>
  719. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  720. <template #default="scope">
  721. <el-tooltip content="修改" placement="top">
  722. <el-button
  723. link
  724. type="primary"
  725. icon="Edit"
  726. @click="handleUpdate(scope.row)"
  727. v-hasPermi="['xg:xxx:edit']"
  728. />
  729. </el-tooltip>
  730. <el-tooltip content="删除" placement="top">
  731. <el-button
  732. link
  733. type="danger"
  734. icon="Delete"
  735. @click="handleDelete(scope.row)"
  736. v-hasPermi="['xg:xxx:remove']"
  737. />
  738. </el-tooltip>
  739. </template>
  740. </el-table-column>
  741. </el-table>
  742. <!-- 分页 -->
  743. <pagination
  744. v-show="total > 0"
  745. :total="total"
  746. v-model:page="queryParams.pageNum"
  747. v-model:limit="queryParams.pageSize"
  748. @pagination="getList"
  749. />
  750. <!-- 添加或修改对话框 -->
  751. <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
  752. <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
  753. <el-form-item label="名称" prop="name">
  754. <el-input v-model="form.name" placeholder="请输入名称" />
  755. </el-form-item>
  756. <el-form-item label="状态" prop="status">
  757. <el-radio-group v-model="form.status">
  758. <el-radio
  759. v-for="dict in sys_normal_disable"
  760. :key="dict.value"
  761. :value="dict.value"
  762. >{{ dict.label }}</el-radio>
  763. </el-radio-group>
  764. </el-form-item>
  765. <el-form-item label="备注" prop="remark">
  766. <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
  767. </el-form-item>
  768. </el-form>
  769. <template #footer>
  770. <div class="dialog-footer">
  771. <el-button @click="cancel">取 消</el-button>
  772. <el-button type="primary" @click="submitForm">确 定</el-button>
  773. </div>
  774. </template>
  775. </el-dialog>
  776. </div>
  777. </template>
  778. <script setup lang="ts" name="Xxx">
  779. import { listXxx, getXxx, addXxx, updateXxx, delXxx, exportXxx } from '@/api/xg/xxx'
  780. import type { XxxQuery, XxxForm, XxxVO } from '@/api/xg/xxx'
  781. // 查询参数
  782. const queryParams = ref<XxxQuery>({
  783. pageNum: 1,
  784. pageSize: 10,
  785. name: undefined,
  786. status: undefined
  787. })
  788. // 表单数据
  789. const form = ref<XxxForm>({
  790. id: undefined,
  791. name: '',
  792. status: '0',
  793. remark: ''
  794. })
  795. // 表单校验规则
  796. const rules = ref({
  797. name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
  798. status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
  799. })
  800. // 数据列表
  801. const xxxList = ref<XxxVO[]>([])
  802. const loading = ref(true)
  803. const total = ref(0)
  804. const showSearch = ref(true)
  805. const single = ref(true)
  806. const multiple = ref(true)
  807. const ids = ref<number[]>([])
  808. // 字典数据
  809. const { sys_normal_disable } = useDict('sys_normal_disable')
  810. // 对话框
  811. const dialog = reactive<DialogOption>({
  812. visible: false,
  813. title: ''
  814. })
  815. const queryFormRef = ref<ElFormInstance>()
  816. const formRef = ref<ElFormInstance>()
  817. /** 查询列表 */
  818. const getList = async () => {
  819. loading.value = true
  820. try {
  821. const res = await listXxx(queryParams.value)
  822. xxxList.value = res.rows
  823. total.value = res.total
  824. } finally {
  825. loading.value = false
  826. }
  827. }
  828. /** 搜索按钮操作 */
  829. const handleQuery = () => {
  830. queryParams.value.pageNum = 1
  831. getList()
  832. }
  833. /** 重置按钮操作 */
  834. const resetQuery = () => {
  835. queryFormRef.value?.resetFields()
  836. handleQuery()
  837. }
  838. /** 多选框选中数据 */
  839. const handleSelectionChange = (selection: XxxVO[]) => {
  840. ids.value = selection.map(item => item.id)
  841. single.value = selection.length !== 1
  842. multiple.value = !selection.length
  843. }
  844. /** 新增按钮操作 */
  845. const handleAdd = () => {
  846. reset()
  847. dialog.visible = true
  848. dialog.title = '添加XXX'
  849. }
  850. /** 修改按钮操作 */
  851. const handleUpdate = async (row?: XxxVO) => {
  852. reset()
  853. const id = row?.id || ids.value[0]
  854. const res = await getXxx(id)
  855. Object.assign(form.value, res.data)
  856. dialog.visible = true
  857. dialog.title = '修改XXX'
  858. }
  859. /** 提交表单 */
  860. const submitForm = () => {
  861. formRef.value?.validate(async (valid) => {
  862. if (valid) {
  863. try {
  864. if (form.value.id) {
  865. await updateXxx(form.value)
  866. proxy?.$modal.msgSuccess('修改成功')
  867. } else {
  868. await addXxx(form.value)
  869. proxy?.$modal.msgSuccess('新增成功')
  870. }
  871. dialog.visible = false
  872. await getList()
  873. } catch (error) {
  874. console.error(error)
  875. }
  876. }
  877. })
  878. }
  879. /** 删除按钮操作 */
  880. const handleDelete = async (row?: XxxVO) => {
  881. const idList = row?.id ? [row.id] : ids.value
  882. await proxy?.$modal.confirm('是否确认删除选中的数据?')
  883. await delXxx(idList)
  884. proxy?.$modal.msgSuccess('删除成功')
  885. await getList()
  886. }
  887. /** 导出按钮操作 */
  888. const handleExport = () => {
  889. proxy?.$download(
  890. 'xg/xxx/export',
  891. { ...queryParams.value },
  892. `xxx_${new Date().getTime()}.xlsx`
  893. )
  894. }
  895. /** 取消按钮 */
  896. const cancel = () => {
  897. dialog.visible = false
  898. reset()
  899. }
  900. /** 表单重置 */
  901. const reset = () => {
  902. form.value = {
  903. id: undefined,
  904. name: '',
  905. status: '0',
  906. remark: ''
  907. }
  908. formRef.value?.resetFields()
  909. }
  910. onMounted(() => {
  911. getList()
  912. })
  913. </script>
  914. ```
  915. **关键点:**
  916. - 使用 `<script setup lang="ts">` 语法
  917. - 使用 `ref` 和 `reactive` 管理状态
  918. - 使用 `v-hasPermi` 指令控制按钮权限
  919. - 使用 `useDict` 获取字典数据
  920. - 使用 `$modal` 显示消息和确认框
  921. - 使用 `$download` 下载导出文件
  922. ### 3. 权限控制
  923. **权限编码规范:** `xg:模块:操作`
  924. 示例:
  925. - `xg:student:list` - 学生列表查询
  926. - `xg:student:query` - 学生详情查询
  927. - `xg:student:add` - 学生新增
  928. - `xg:student:edit` - 学生修改
  929. - `xg:student:remove` - 学生删除
  930. - `xg:student:export` - 学生导出
  931. - `xg:student:import` - 学生导入
  932. **使用方式:**
  933. ```vue
  934. <!-- 按钮权限控制 -->
  935. <el-button v-hasPermi="['xg:student:add']">新增</el-button>
  936. <!-- JS中判断权限 -->
  937. <script setup>
  938. import { hasPermi } from '@/utils/permission'
  939. const canEdit = hasPermi(['xg:student:edit'])
  940. </script>
  941. ```
  942. ---
  943. ## 📝 开发流程指南
  944. ### 1. 新建学工功能模块完整流程
  945. #### 后端开发步骤:
  946. 1. **创建数据库表**(表名格式:`xg_模块名`)
  947. 2. **创建 Entity 实体类**(`domain/XgXxx.java`)
  948. 3. **创建 VO 视图对象**(`domain/vo/XgXxxVo.java`)
  949. 4. **创建 BO 业务对象**(`domain/bo/XgXxxBo.java`)
  950. 5. **创建 Mapper 接口**(`mapper/XgXxxMapper.java`)
  951. 6. **创建 Mapper XML**(`resources/mapper/xg/XgXxxMapper.xml`,如需自定义SQL)
  952. 7. **创建 Service 接口**(`service/IXgXxxService.java`)
  953. 8. **创建 Service 实现**(`service/impl/XgXxxServiceImpl.java`)
  954. 9. **创建 Controller 控制器**(`controller/XgXxxController.java`)
  955. 10. **在菜单管理中配置菜单和权限**
  956. #### 前端开发步骤:
  957. 1. **创建 API 接口文件**(`api/xg/xxx.ts`)
  958. 2. **创建页面文件**(`views/xg/xxx/index.vue`)
  959. 3. **配置路由**(通过后端菜单管理配置,前端动态加载)
  960. 4. **测试功能**
  961. ### 2. 使用代码生成器(推荐)
  962. 框架提供了强大的代码生成器,可以一键生成所有代码:
  963. 1. 登录系统,进入 **系统工具 -> 代码生成**
  964. 2. 点击 **导入表**,选择要生成的数据库表
  965. 3. 点击 **编辑**,配置生成信息:
  966. - 基本信息:包名、模块名、作者等
  967. - 字段信息:字段类型、显示类型、是否必填等
  968. - 生成信息:生成模板、前端路径等
  969. 4. 点击 **生成代码**,下载代码包
  970. 5. 将生成的代码解压到对应目录
  971. 6. 重启后端服务,刷新前端页面
  972. ---
  973. ## 🎯 学工模块常见业务场景
  974. ### 1. 学生信息管理
  975. - **表命名**: `xg_student`, `xg_student_msg`
  976. - **权限前缀**: `xg:student`
  977. - **API路径**: `/xg/student`
  978. - **页面路径**: `views/xg/xsjcxx`(学生基础信息)
  979. ### 2. 宿舍管理
  980. - **表命名**: `xg_dorm_*`(校区、楼栋、房间、床位等)
  981. - **权限前缀**: `xg:dorm`
  982. - **API路径**: `/xg/dorm`
  983. - **页面路径**: `views/xg/xgss`(学工宿舍)
  984. ### 3. 班级管理
  985. - **表命名**: `xg_class_*`
  986. - **权限前缀**: `xg:class`
  987. - **API路径**: `/xg/class`
  988. - **页面路径**: `views/xg/class`
  989. ### 4. 统计分析
  990. - **表命名**: 可直接查询其他表统计
  991. - **权限前缀**: `xg:statistics`
  992. - **API路径**: `/xg/statistics` 或 `/xg/studentStatistics`
  993. - **页面路径**: `views/xg/studentStatistics`
  994. ---
  995. ## 🔧 常用工具类和方法
  996. ### 后端常用工具
  997. **字符串工具 - StringUtils**
  998. ```java
  999. import org.dromara.common.core.utils.StringUtils;
  1000. StringUtils.isNotBlank(str) // 判断字符串不为空
  1001. StringUtils.isBlank(str) // 判断字符串为空
  1002. StringUtils.isEmpty(str) // 判断字符串是否为null或空串
  1003. ```
  1004. **对象转换 - MapstructUtils**
  1005. ```java
  1006. import org.dromara.common.core.utils.MapstructUtils;
  1007. // BO 转 Entity
  1008. XgStudent entity = MapstructUtils.convert(bo, XgStudent.class);
  1009. // Entity 转 VO
  1010. XgStudentVo vo = MapstructUtils.convert(entity, XgStudentVo.class);
  1011. // List 转换
  1012. List<XgStudentVo> voList = MapstructUtils.convert(entityList, XgStudentVo.class);
  1013. ```
  1014. **日期工具 - DateUtils**
  1015. ```java
  1016. import org.dromara.common.core.utils.DateUtils;
  1017. DateUtils.parseDate(str) // 解析日期字符串
  1018. DateUtils.parseDateToStr(date) // 日期转字符串
  1019. DateUtils.getNowDate() // 获取当前日期
  1020. ```
  1021. **Excel导出 - ExcelUtil**
  1022. ```java
  1023. import org.dromara.common.excel.utils.ExcelUtil;
  1024. ExcelUtil.exportExcel(list, "标题", XgXxxVo.class, response);
  1025. ```
  1026. ### 前端常用工具
  1027. **日期格式化**
  1028. ```typescript
  1029. import { parseTime } from '@/utils/ruoyi'
  1030. parseTime(new Date()) // 格式化日期时间
  1031. parseTime(date, '{y}-{m}-{d}') // 自定义格式
  1032. ```
  1033. **权限判断**
  1034. ```typescript
  1035. import { hasPermi, hasRole } from '@/utils/permission'
  1036. hasPermi(['xg:student:edit']) // 判断是否有权限
  1037. hasRole(['admin']) // 判断是否有角色
  1038. ```
  1039. **字典标签**
  1040. ```vue
  1041. <dict-tag :options="sys_normal_disable" :value="status" />
  1042. ```
  1043. **文件下载**
  1044. ```typescript
  1045. proxy?.$download('api/url', params, 'filename.xlsx')
  1046. ```
  1047. ---
  1048. ## 🚨 注意事项
  1049. ### 后端开发注意事项
  1050. 1. **权限注解必须添加**:所有 Controller 方法都必须添加 `@SaCheckPermission` 注解
  1051. 2. **操作日志记录**:重要操作(增删改)必须添加 `@Log` 注解
  1052. 3. **防重复提交**:新增/修改操作建议添加 `@RepeatSubmit` 注解
  1053. 4. **参数校验**:使用 `@Validated` 和校验注解(@NotNull、@NotBlank等)
  1054. 5. **事务管理**:涉及多表操作的 Service 方法要添加 `@Transactional` 注解
  1055. 6. **逻辑删除**:删除操作优先使用逻辑删除(delFlag字段)
  1056. 7. **租户隔离**:继承 `TenantEntity` 实现多租户数据隔离
  1057. 8. **SQL注入防护**:使用 MyBatis-Plus 的 Wrapper 构建查询条件,避免字符串拼接
  1058. ### 前端开发注意事项
  1059. 1. **权限控制**:所有操作按钮必须添加 `v-hasPermi` 指令
  1060. 2. **表单校验**:表单提交前必须进行前端校验
  1061. 3. **加载状态**:异步操作要显示 loading 状态
  1062. 4. **错误处理**:API 调用要用 try-catch 捕获异常
  1063. 5. **代码规范**:使用 TypeScript,定义好接口类型
  1064. 6. **组件复用**:相同功能的组件要抽取为公共组件
  1065. 7. **国际化**:涉及到文本的地方考虑国际化支持
  1066. 8. **响应式布局**:页面要考虑不同屏幕尺寸的适配
  1067. ### 学工模块特殊注意
  1068. 1. **学生隐私保护**:身份证号、手机号等敏感信息需要脱敏处理
  1069. 2. **数据权限控制**:根据用户角色(辅导员、班主任等)限制数据访问范围
  1070. 3. **批量操作优化**:大量学生数据的批量导入/导出要考虑性能优化
  1071. 4. **工作流集成**:涉及审批流程的功能需要集成工作流引擎
  1072. 5. **数据统计性能**:统计分析功能要注意查询性能,必要时使用缓存
  1073. ---
  1074. ## 📚 参考资源
  1075. ### 官方文档
  1076. - **RuoYi-Vue-Plus 文档**: https://plus-doc.dromara.org
  1077. - **Vue 3 文档**: https://cn.vuejs.org
  1078. - **Element Plus 文档**: https://element-plus.org/zh-CN
  1079. - **MyBatis-Plus 文档**: https://baomidou.com
  1080. - **Sa-Token 文档**: https://sa-token.cc
  1081. ### 框架特色功能文档
  1082. - **多租户**: https://plus-doc.dromara.org/#/ruoyi-vue-plus/framework/tenant
  1083. - **数据权限**: https://plus-doc.dromara.org/#/ruoyi-vue-plus/framework/data_permission
  1084. - **数据脱敏**: https://plus-doc.dromara.org/#/ruoyi-vue-plus/framework/sensitive
  1085. - **工作流**: https://plus-doc.dromara.org/#/ruoyi-vue-plus/framework/workflow
  1086. - **代码生成器**: https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/generator
  1087. ---
  1088. ## 🎓 总结
  1089. 遵循本规范文件,AI可以:
  1090. 1. **理解项目整体架构**:前后端分离、模块化设计
  1091. 2. **快速生成标准代码**:Controller、Service、Mapper、Vue页面等
  1092. 3. **保持代码风格统一**:命名规范、注解使用、权限控制等
  1093. 4. **处理学工业务场景**:学生管理、宿舍管理、统计分析等
  1094. 5. **避免常见错误**:权限遗漏、参数校验缺失、SQL注入等
  1095. **开发原则**:
  1096. - ✅ 代码规范统一
  1097. - ✅ 安全性优先
  1098. - ✅ 性能优化考虑
  1099. - ✅ 用户体验良好
  1100. - ✅ 可维护性强
  1101. **生成代码时的核心要求**:
  1102. 1. 严格遵循本文档的命名规范和代码结构
  1103. 2. 必须包含完整的权限控制和参数校验
  1104. 3. 前后端接口定义要保持一致
  1105. 4. 注释要清晰完整
  1106. 5. 考虑学工业务的特殊性(隐私保护、数据权限等)