You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
8.0 KiB

10 months ago
10 months ago
  1. const prisma = require("../utils/prisma");
  2. /**
  3. * @typedef {Object} Dept
  4. * @property {number} deptId
  5. * @property {number} parentId
  6. * @property {string} ancestors
  7. * @property {string} deptName
  8. * @property {number} orderNum
  9. * @property {number} status
  10. * @property {number} delFlag
  11. * @property {Date} createdAt
  12. * @property {Date} lastUpdatedAt
  13. */
  14. const Dept = {
  15. writable: [
  16. 'parentId', 'ancestors', 'deptName', 'orderNum', 'status', 'delFlag'
  17. ],
  18. validations: {
  19. deptName: (newValue = '') => {
  20. if (typeof newValue !== 'string' || newValue.length > 255) {
  21. throw new Error('Dept name must be a string and cannot be longer than 255 characters');
  22. }
  23. return newValue;
  24. },
  25. orderNum: (newValue = 0) => {
  26. const num = Number(newValue);
  27. if (isNaN(num)) {
  28. throw new Error('Order num must be a number');
  29. }
  30. return num;
  31. },
  32. status: (newValue = 0) => {
  33. const status = Number(newValue);
  34. if (isNaN(status) || status < 0 || status > 1) {
  35. throw new Error('Status must be a number between 0 and 1');
  36. }
  37. return status;
  38. },
  39. delFlag: (newValue = 0) => {
  40. const flag = Number(newValue);
  41. if (isNaN(flag) || flag < 0 || flag > 1) {
  42. throw new Error('Del flag must be a number between 0 and 1');
  43. }
  44. return flag;
  45. }
  46. },
  47. castColumnValue: function (key, value) {
  48. switch (key) {
  49. case 'status':
  50. case 'delFlag':
  51. return Number(value);
  52. default:
  53. return value;
  54. }
  55. },
  56. create: async function ({ parentId, ancestors, deptName, orderNum, status = 0, delFlag = 0 }) {
  57. try {
  58. const validatedDeptName = this.validations.deptName(deptName);
  59. const validatedOrderNum = this.validations.orderNum(orderNum);
  60. const validatedStatus = this.validations.status(status);
  61. const validatedDelFlag = this.validations.delFlag(delFlag);
  62. const dept = await prisma.dept.create({
  63. data: {
  64. parentId,
  65. ancestors,
  66. deptName: validatedDeptName,
  67. orderNum: validatedOrderNum,
  68. status: validatedStatus,
  69. delFlag: validatedDelFlag,
  70. createdAt: new Date(),
  71. lastUpdatedAt: new Date()
  72. }
  73. });
  74. return { dept, error: null };
  75. } catch (error) {
  76. console.error('FAILED TO CREATE DEPT.', error.message);
  77. return { dept: null, error: error.message };
  78. }
  79. },
  80. // 插入部门数据
  81. insertDept: async function (dept) {
  82. try {
  83. const insertedDept = await prisma.dept.create({
  84. data: {
  85. deptName: dept.deptName,
  86. parentId: dept.parentId || null,
  87. ancestors: dept.ancestors || null,
  88. orderNum: dept.orderNum || 0,
  89. status: dept.status || 0,
  90. delFlag: dept.delFlag || 0,
  91. createdAt: new Date(),
  92. lastUpdatedAt: new Date(),
  93. },
  94. });
  95. return insertedDept;
  96. } catch (error) {
  97. console.error("插入部门数据失败:", error);
  98. throw error;
  99. }
  100. },
  101. update: async function (deptId, updates = {}) {
  102. try {
  103. if (!deptId) throw new Error('No dept id provided for update');
  104. const currentDept = await prisma.dept.findUnique({ where: { deptId } });
  105. if (!currentDept) throw new Error('Dept not found');
  106. Object.entries(updates).forEach(([key, value]) => {
  107. if (this.writable.includes(key)) {
  108. if (Object.prototype.hasOwnProperty.call(this.validations, key)) {
  109. updates[key] = this.validations[key](this.castColumnValue(key, value));
  110. } else {
  111. updates[key] = this.castColumnValue(key, value);
  112. }
  113. }
  114. });
  115. updates.lastUpdatedAt = new Date();
  116. const updatedDept = await prisma.dept.update({ where: { deptId }, data: updates });
  117. return { success: true, error: null, dept: updatedDept };
  118. } catch (error) {
  119. console.error(error.message);
  120. return { success: false, error: error.message, dept: null };
  121. }
  122. },
  123. get: async function (clause = {}) {
  124. try {
  125. const dept = await prisma.dept.findFirst({ where: clause, select: {
  126. deptId: true, parentId: true, ancestors: true, deptName: true, orderNum: true, status: true, delFlag: true, createdAt: true, lastUpdatedAt: true
  127. } });
  128. return dept ? { dept } : null;
  129. } catch (error) {
  130. console.error(error.message);
  131. return null;
  132. }
  133. },
  134. delete: async function (clause = {}) {
  135. try {
  136. const affectedRows = await prisma.dept.deleteMany({ where: clause });
  137. return affectedRows > 0;
  138. } catch (error) {
  139. console.error(error.message);
  140. return false;
  141. }
  142. },
  143. where: async function (clause = {}, limit = null) {
  144. try {
  145. const depts = await prisma.dept.findMany({
  146. where: clause,
  147. take: limit !== null ? limit : undefined,
  148. select: {
  149. deptId: true, parentId: true, ancestors: true, deptName: true, orderNum: true, status: true, delFlag: true, createdAt: true, lastUpdatedAt: true
  150. }
  151. });
  152. return depts;
  153. } catch (error) {
  154. console.error(error.message);
  155. return [];
  156. }
  157. },
  158. checkDeptNameUnique: async function (dept){
  159. try {
  160. const existingDept = await prisma.dept.findFirst({
  161. where: {
  162. deptName: dept.deptName, // 根据部门名称查询
  163. parentId: dept.parentId, // 排除父id
  164. },
  165. });
  166. // 如果查询到记录,说明部门名称已存在
  167. return !existingDept;
  168. } catch (error) {
  169. console.error('检查部门名称唯一性失败:', error);
  170. throw error;
  171. }
  172. },
  173. // 检查部门是否包含未停用的子部门
  174. selectNormalChildrenDeptById: async function (deptId) {
  175. try {
  176. // 查询所有祖先部门中包含当前部门的未停用部门
  177. const childrenDepts = await prisma.$queryRaw`
  178. SELECT COUNT(*) as count
  179. FROM sys_dept
  180. WHERE status = 0
  181. AND del_flag = '0'
  182. AND FIND_IN_SET(${deptId}, ancestors)
  183. `;
  184. // 返回未停用的子部门数量
  185. return childrenDepts[0].count;
  186. } catch (error) {
  187. console.error("查询子部门失败:", error);
  188. throw error;
  189. }
  190. },
  191. // 检查部门是否有子部门
  192. hasChildByDeptId: async function (deptId) {
  193. try {
  194. const children = await prisma.dept.findMany({
  195. where: {
  196. parentId: deptId, // 查询当前部门的子部门
  197. delFlag: 0,
  198. },
  199. });
  200. // 如果有子部门,返回 true
  201. return children.length > 0;
  202. } catch (error) {
  203. console.error("检查子部门失败:", error);
  204. throw error;
  205. }
  206. },
  207. // 检查部门是否存在用户
  208. checkDeptExistUser: async function (deptId) {
  209. try {
  210. const users = await prisma.user.findMany({
  211. where: {
  212. deptId: deptId, // 查询当前部门的用户
  213. },
  214. });
  215. // 如果存在用户,返回 true
  216. return users.length > 0;
  217. } catch (error) {
  218. console.error("检查部门用户失败:", error);
  219. throw error;
  220. }
  221. },
  222. /**
  223. * 获取部门树状结构
  224. * @returns {Promise<Array>}
  225. */
  226. getDeptTree:async function () {
  227. try {
  228. // 查询所有部门
  229. const allDepts = await prisma.dept.findMany();
  230. // 构建树状结构
  231. const buildTree = (parentId = null) => {
  232. return allDepts
  233. .filter((dept) => dept.parentId === parentId)
  234. .map((dept) => ({
  235. ...dept,
  236. children: buildTree(dept.deptId), // 递归获取子部门
  237. }));
  238. };
  239. return buildTree();
  240. } catch (error) {
  241. console.error("获取部门树状结构失败:", error);
  242. throw error;
  243. }
  244. },
  245. /**
  246. * 根据父部门 ID 获取子部门列表
  247. * @param {number} parentId - 父部门 ID
  248. * @returns {Promise<Array>}
  249. */
  250. getChildrenByParentId:async function (parentId = null) {
  251. try {
  252. const children = await prisma.dept.findMany({
  253. where: { parentId },
  254. });
  255. return children;
  256. } catch (error) {
  257. console.error("获取子部门列表失败:", error);
  258. throw error;
  259. }
  260. },
  261. };
  262. module.exports = { Dept };