sub_project.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const rootId = '-1';
  10. const imType = require('../const/tender').imType;
  11. const defaultFunRela = {
  12. banOver: true,
  13. hintOver: true,
  14. banMinusChangeBills: true,
  15. minusNoValue: true,
  16. lockPayExpr: false,
  17. showMinusCol: true,
  18. imType: imType.zl.value,
  19. needGcl: false,
  20. };
  21. const funSet = require('../const/fun_set');
  22. const defaultFunSet = funSet.defaultInfo;
  23. const pageShowConst = require('../const/page_show').defaultSetting;
  24. module.exports = app => {
  25. class SubProject extends app.BaseService {
  26. /**
  27. * 构造函数
  28. *
  29. * @param {Object} ctx - egg全局变量
  30. * @param {String} tableName - 表名
  31. * @return {void}
  32. */
  33. constructor(ctx) {
  34. super(ctx);
  35. this.tableName = 'sub_project';
  36. }
  37. async getSubProject(pid, uid, admin, filterFolder = false) {
  38. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  39. const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
  40. result = result.filter(x => {
  41. if (x.is_folder) return !filterFolder;
  42. if (admin) return true;
  43. const pb = permission.find(y => { return x.id === y.spid});
  44. if (!pb) return false;
  45. x.user_permission = pb;
  46. return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.user_permission.manage_permission.length > 0;
  47. });
  48. return result;
  49. }
  50. _filterEmptyFolder(data) {
  51. data.sort((a, b) => { return b.tree_level - a.tree_level});
  52. const result = [];
  53. for (const d of data) {
  54. if (!d.is_folder) result.push(d);
  55. if (result.find(x => { return x.tree_pid === d.id; })) result.push(d);
  56. }
  57. return result;
  58. }
  59. async getBudgetProject(pid, uid, admin) {
  60. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  61. const adminPermission = this.ctx.service.subProjPermission.adminPermission;
  62. const permission = admin ? [] : await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
  63. result = result.filter(x => {
  64. if (!x.is_folder && !x.budget_id) return false;
  65. if (x.is_folder) return true;
  66. if (admin) {
  67. x.permission = adminPermission.budget_permission;
  68. x.manage_permission = adminPermission.manage_permission;
  69. return true;
  70. } else {
  71. const pb = permission.find(y => { return x.id === y.spid});
  72. if (!pb) return false;
  73. x.permission = pb.budget_permission;
  74. x.manage_permission = pb.manage_permission;
  75. return x.permission.length > 0;
  76. }
  77. });
  78. return this._filterEmptyFolder(result);
  79. }
  80. async getFileProject(pid, uid, admin) {
  81. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  82. const adminPermission = this.ctx.service.subProjPermission.adminPermission;
  83. const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
  84. result = result.filter(x => {
  85. if (!x.is_folder && !x.management) return false;
  86. if (x.is_folder) return true;
  87. if (admin) {
  88. x.permission = adminPermission.file_permission;
  89. x.manage_permission = adminPermission.manage_permission;
  90. return true;
  91. } else {
  92. const pb = permission.find(y => { return x.id === y.spid});
  93. if (!pb) return false;
  94. x.permission = pb.file_permission;
  95. x.manage_permission = pb.manage_permission;
  96. return x.permission.length > 0;
  97. }
  98. });
  99. return this._filterEmptyFolder(result);
  100. }
  101. async getLastChild(tree_pid) {
  102. const result = await this.getAllDataByCondition({ where: { tree_pid, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'desc']], limit: 1, offset: 0 });
  103. return result[0];
  104. }
  105. async getPosterityData(id){
  106. const result = [];
  107. let cur = await this.getAllDataByCondition({ where: { tree_pid: id, project_id: this.ctx.session.sessionProject.id } });
  108. let iLevel = 1;
  109. while (cur.length > 0 && iLevel < 6) {
  110. result.push(...cur);
  111. cur = await this.getAllDataByCondition({ where: { tree_pid: cur.map(x => { return x.id })} });
  112. iLevel += 1;
  113. }
  114. return result;
  115. }
  116. async getStepNode(node, step) {
  117. const tree_order = [];
  118. while(step) {
  119. tree_order.push(node.tree_order + step);
  120. if (step > 0) {
  121. step = step - 1;
  122. } else {
  123. step = step + 1;
  124. }
  125. }
  126. return await this.getAllDataByCondition({ where: { tree_pid: node.tree_pid, tree_order, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'asc']]});
  127. }
  128. async addFolder(data) {
  129. const parent = await this.getDataById(data.tree_pid);
  130. if (parent && !parent.is_folder) throw '添加数据结构错误';
  131. const lastChild = await this.getLastChild(parent ? parent.id : rootId);
  132. const conn = await this.db.beginTransaction();
  133. try {
  134. // 获取当前用户信息
  135. const sessionUser = this.ctx.session.sessionUser;
  136. // 获取当前项目信息
  137. const sessionProject = this.ctx.session.sessionProject;
  138. const insertData = {
  139. id: this.uuid.v4(), project_id: sessionProject.id, user_id: sessionUser.accountId,
  140. tree_pid: data.tree_pid,
  141. tree_level: parent ? parent.tree_level + 1 : 1,
  142. tree_order: lastChild ? lastChild.tree_order + 1 : 1,
  143. name: data.name, is_folder: 1,
  144. };
  145. const operate = await conn.insert(this.tableName, insertData);
  146. if (operate.affectedRows === 0) throw '新增文件夹失败';
  147. await conn.commit();
  148. return await this.getSubProject(sessionProject.id, sessionUser.accountId, sessionUser.is_admin);
  149. } catch (error) {
  150. await conn.rollback();
  151. throw error;
  152. }
  153. }
  154. async addSubProject(data) {
  155. const parent = await this.getDataById(data.tree_pid);
  156. if (parent && !parent.is_folder) throw '添加数据结构错误';
  157. const lastChild = await this.getLastChild(parent ? parent.id : rootId);
  158. const conn = await this.db.beginTransaction();
  159. try {
  160. // 获取当前用户信息
  161. const sessionUser = this.ctx.session.sessionUser;
  162. // 获取当前项目信息
  163. const sessionProject = this.ctx.session.sessionProject;
  164. const insertData = {
  165. id: this.uuid.v4(), project_id: sessionProject.id, user_id: sessionUser.accountId,
  166. tree_pid: data.tree_pid,
  167. tree_level: parent ? parent.tree_level + 1 : 1,
  168. tree_order: lastChild ? lastChild.tree_order + 1 : 1,
  169. name: data.name, is_folder: 0,
  170. };
  171. const operate = await conn.insert(this.tableName, insertData);
  172. // todo 根据节点新增时的其他操作
  173. if (operate.affectedRows === 0) throw '新增文件夹失败';
  174. await conn.commit();
  175. return await this.getSubProject(sessionProject.id, sessionUser.accountId, sessionUser.is_admin);
  176. } catch (error) {
  177. await conn.rollback();
  178. throw error;
  179. }
  180. }
  181. async dragTo(data) {
  182. const dragNode = await this.getDataById(data.drag_id);
  183. const dropNode = await this.getDataById(data.drop_id);
  184. if (!dragNode || !dropNode || !dropNode.is_folder) throw '拖拽数据结构错误';
  185. const lastChild = await this.getLastChild(dropNode.id);
  186. const posterity = await this.getPosterityData(dragNode.id);
  187. const conn = await this.db.beginTransaction();
  188. try {
  189. const updateData = {
  190. id: dragNode.id, tree_pid: dropNode.id, tree_level: dropNode.tree_level + 1,
  191. tree_order: lastChild ? lastChild.tree_order + 1 : 1,
  192. };
  193. await conn.update(this.tableName, updateData);
  194. if (dragNode.tree_level !== dropNode.tree_level + 1 && posterity.length > 0) {
  195. const posterityUpdateData = posterity.map(x => {
  196. return { id: x.id, tree_level: dropNode.tree_level + 1 - dragNode.tree_level + x.tree_level }
  197. });
  198. await conn.updateRows(this.tableName, posterityUpdateData);
  199. }
  200. // 升级原来的后项的order
  201. await conn.query(`UPDATE ${this.tableName} SET tree_order = tree_order-1 WHERE tree_pid = ? AND tree_order > ?`, [dragNode.tree_pid, dragNode.tree_order]);
  202. await conn.commit();
  203. } catch (error) {
  204. await conn.rollback();
  205. throw error;
  206. }
  207. return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin);
  208. }
  209. async _siblingMove(node, step) {
  210. const stepNode = await this.getStepNode(node, step);
  211. const conn = await this.db.beginTransaction();
  212. try {
  213. const updateData = [];
  214. updateData.push({ id: node.id, tree_order: node.tree_order + step });
  215. for (const sn of stepNode) {
  216. updateData.push({ id: node.id, tree_order: step > 0 ? sn.tree_order - 1 : sn.tree_order + 1 });
  217. }
  218. await conn.updateRows(this.tableName, updateData);
  219. await conn.commit();
  220. } catch (error) {
  221. await conn.rollback();
  222. throw error;
  223. }
  224. }
  225. async _siblingMoveForce(node, step) {
  226. const sibling = await this.getAllDataByCondition({ where: { tree_pid: node.tree_pid, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'asc']] });
  227. const nodeIndex = sibling.findIndex(x => { return x.id === node.id });
  228. if (nodeIndex + step < 0) throw '移动数据结构错误';
  229. if (nodeIndex + step > sibling.length - 1) throw '移动数据结构错误';
  230. const conn = await this.db.beginTransaction();
  231. try {
  232. const updateData = [];
  233. updateData.push({ id: node.id, tree_order: sibling[nodeIndex + step].tree_order });
  234. while(step) {
  235. const stepNode = sibling[nodeIndex + step];
  236. if (step > 0) {
  237. updateData.push({ id: stepNode.id, tree_order: sibling[nodeIndex + step - 1].tree_order });
  238. step = step - 1;
  239. } else {
  240. updateData.push({ id: stepNode.id, tree_order: sibling[nodeIndex + step + 1].tree_order});
  241. step = step + 1;
  242. }
  243. }
  244. await conn.updateRows(this.tableName, updateData);
  245. await conn.commit();
  246. } catch (error) {
  247. await conn.rollback();
  248. throw error;
  249. }
  250. }
  251. async _topMove(node) {
  252. const lastChild = await this.getLastChild(rootId);
  253. const posterity = await this.getPosterityData(node.id);
  254. const conn = await this.db.beginTransaction();
  255. try {
  256. const updateData = { id: node.id, tree_pid: rootId, tree_level: 1, tree_order: lastChild ? lastChild.tree_order + 1 : 1 };
  257. await conn.update(this.tableName, updateData);
  258. if (node.tree_level !== 1 && posterity.length > 0) {
  259. const posterityUpdateData = posterity.map(x => {
  260. return { id: x.id, tree_level: x.tree_level - node.tree_level + 1 }
  261. });
  262. await conn.updateRows(this.tableName, posterityUpdateData);
  263. }
  264. // 升级原来的后项的order
  265. await conn.query(`UPDATE ${this.tableName} SET tree_order = tree_order-1 WHERE tree_pid = ? AND tree_order > ?`, [node.tree_pid, node.tree_order]);
  266. await conn.commit();
  267. } catch (error) {
  268. await conn.rollback();
  269. throw error;
  270. }
  271. }
  272. async move(data) {
  273. const node = await this.getDataById(data.id);
  274. if (!node) throw '移动数据结构错误';
  275. switch(data.type) {
  276. case 'up': await this._siblingMoveForce(node, -1); break;
  277. case 'down': await this._siblingMoveForce(node, 1); break;
  278. case 'top': await this._topMove(node); break;
  279. default: throw '未知移动类型';
  280. }
  281. return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin);
  282. }
  283. async del(id) {
  284. const node = await this.getDataById(id);
  285. if (!node) throw '删除的数据不存在';
  286. const posterity = await this.getPosterityData(node.id);
  287. const updateData = [
  288. { id: node.id, is_delete: 1 },
  289. ];
  290. posterity.forEach(x => {
  291. updateData.push({ id: x.id, is_delete: 1});
  292. });
  293. await this.db.updateRows(this.tableName, updateData);
  294. return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin);
  295. }
  296. async save(data) {
  297. const result = await this.db.update(this.tableName, data);
  298. if (result.affectedRows > 0) {
  299. return data;
  300. } else {
  301. throw '更新数据失败';
  302. }
  303. }
  304. async setBudgetStd(data) {
  305. const subProject = await this.getDataById(data.id);
  306. const budgetStd = await this.ctx.service.budgetStd.getDataById(data.std_id);
  307. if (!budgetStd) throw '选择的概算标准不存在,请刷新页面重试';
  308. const conn = await this.db.beginTransaction();
  309. try {
  310. const budget_id = await this.ctx.service.budget.add(conn, {
  311. pid: subProject.project_id, user_id: subProject.user_id, rela_tender: subProject.rela_tender
  312. }, budgetStd);
  313. const updateData = { id: data.id, std_id: budgetStd.id, std_name: budgetStd.name, budget_id };
  314. await conn.update(this.tableName, updateData);
  315. await conn.commit();
  316. return updateData;
  317. } catch (error) {
  318. await conn.rollback();
  319. throw error;
  320. }
  321. }
  322. async setRelaTender(data) {
  323. const subProject = await this.getDataById(data.id);
  324. const orgRelaTenderId = subProject.rela_tender.split(',');
  325. const conn = await this.db.beginTransaction();
  326. try {
  327. await conn.update(this.tableName, data);
  328. await conn.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender: data.rela_tender });
  329. const relaTenderId = data.rela_tender.split(',');
  330. const removeTenderId = orgRelaTenderId.filter(x => { return relaTenderId.indexOf(x) < 0});
  331. const addTenderId = relaTenderId.filter(x => { return orgRelaTenderId.indexOf(x) < 0});
  332. if (removeTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: '' }, { where: { id: removeTenderId }});
  333. if (addTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: data.id }, { where: { id: addTenderId }});
  334. await conn.commit();
  335. return data;
  336. } catch (error) {
  337. await conn.rollback();
  338. throw error;
  339. }
  340. }
  341. async addRelaTender(transaction, spid, tid) {
  342. if (!transaction) throw '未定义事务';
  343. const subProject = await this.getDataById(spid);
  344. if (!subProject) throw '所属项目不存在';
  345. const rela = subProject.rela_tender.split(',');
  346. if (rela.indexOf(tid + '') >= 0) return;
  347. rela.push(tid + '');
  348. const rela_tender = rela.join(',');
  349. await transaction.update(this.tableName, { id: spid, rela_tender});
  350. await transaction.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender});
  351. }
  352. async removeRelaTender(transaction, spid, tid) {
  353. if (!transaction) throw '未定义事务';
  354. const subProject = await this.getDataById(spid);
  355. if (!subProject) throw '所属项目不存在';
  356. const rela = subProject.rela_tender.split(',');
  357. if (rela.indexOf(tid + '') < 0) return;
  358. const rela_tender = rela.filter(x => { return x === tid + ''}).join(',');
  359. await transaction.update(this.tableName, { id: spid, rela_tender});
  360. await transaction.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender});
  361. }
  362. async setManagement(data) {
  363. const subProject = await this.getDataById(data.id);
  364. if (subProject.management === data.management) return data;
  365. const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: data.management }});
  366. const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } });
  367. const dm = [], um = [], im = [];
  368. const template = await this.ctx.service.filingTemplateList.getDataById(data.filingTemplate);
  369. if (!template) throw '选择的文件类别不存在';
  370. const templateFiling = await this.ctx.service.filingTemplate.getAllDataByCondition({
  371. where: { temp_id: template.id, is_fixed: 1 },
  372. });
  373. const filing_type = this.ctx.service.filing.analysisFilingType(templateFiling).map(x => { return x.value; }).join(','), file_permission = '1,2';
  374. for (const u of users) {
  375. const nm = orgMember.find(x => { return u.id === x.uid; });
  376. if (nm) {
  377. if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type });
  378. } else {
  379. im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type });
  380. }
  381. }
  382. const conn = await this.db.beginTransaction();
  383. try {
  384. await conn.update(this.tableName, { id: subProject.id, management: data.management, filing_template_id: template.id, filing_template_name: template.name });
  385. await this.ctx.service.filing.initFiling(subProject.id, data.filingTemplate, conn);
  386. if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm });
  387. if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um);
  388. if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im);
  389. await conn.commit();
  390. return data;
  391. } catch (error) {
  392. await conn.rollback();
  393. throw error;
  394. }
  395. }
  396. async refreshManagementPermission(data) {
  397. const subProject = await this.getDataById(data.id);
  398. const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: subProject.management }});
  399. const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } });
  400. const dm = [], um = [], im = [];
  401. const filing_type = this.ctx.service.filing.allFilingType.join(','), file_permission = '1,2';
  402. for (const u of users) {
  403. const nm = orgMember.find(x => { return u.id === x.uid; });
  404. if (nm) {
  405. if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type });
  406. } else {
  407. im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type });
  408. }
  409. }
  410. const conn = await this.db.beginTransaction();
  411. try {
  412. if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm });
  413. if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um);
  414. if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im);
  415. await conn.commit();
  416. return { dm: dm.length, um: um.length, im: im.length };
  417. } catch (error) {
  418. await conn.rollback();
  419. throw error;
  420. }
  421. }
  422. // 合同管理获取项目列表
  423. async getSubProjectByContract(pid, uid, admin, filterFolder = false) {
  424. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  425. if (admin) return this._filterEmptyFolder(result);
  426. const permission = await this.ctx.service.contractAudit.getAllDataByCondition({ where: { uid } });
  427. result = result.filter(x => {
  428. if (x.is_folder) return !filterFolder;
  429. const pb = permission.find(y => { return x.id === y.spid; });
  430. if (!pb) return false;
  431. return true;
  432. });
  433. return this._filterEmptyFolder(result);
  434. }
  435. async getSubProjectByTender(pid, tenders, filterFolder = false) {
  436. if (tenders.length === 0) return [];
  437. const spids = this._.uniq(this._.map(tenders, 'spid'));
  438. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  439. result = result.filter(x => {
  440. if (x.is_folder) return !filterFolder;
  441. if (!x.rela_tender) return false;
  442. return this._.includes(spids, x.id);
  443. });
  444. return this._filterEmptyFolder(result);
  445. }
  446. // 合同管理获取项目列表
  447. async getSubProjectByFinancial(pid, uid, admin, filterFolder = false) {
  448. let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
  449. if (admin) return this._filterEmptyFolder(result);
  450. const permission = await this.ctx.service.financialAudit.getAllDataByCondition({ where: { uid } });
  451. result = result.filter(x => {
  452. if (x.is_folder) return !filterFolder;
  453. const pb = permission.find(y => { return x.id === y.spid; });
  454. if (!pb) return false;
  455. return true;
  456. });
  457. return this._filterEmptyFolder(result);
  458. }
  459. async getFileReference(subProject) {
  460. return await this.db.query(`SELECT id, name FROM zh_file_reference_list`);
  461. };
  462. getPageShow(page_show) {
  463. const info = page_show ? JSON.parse(page_show) : {};
  464. for (const pi in pageShowConst) {
  465. info[pi] = info[pi] === undefined ? pageShowConst[pi] : parseInt(info[pi]);
  466. this.ctx.helper._.defaults(info[pi], pageShowConst[pi]);
  467. }
  468. return info;
  469. }
  470. /**
  471. * 功能设置
  472. * @param id
  473. * @returns {Promise<null>}
  474. */
  475. getFunRela(subProject) {
  476. const result = subProject.fun_rela ? JSON.parse(subProject.fun_rela) : {};
  477. this.ctx.helper._.defaults(result, defaultFunRela);
  478. return result;
  479. }
  480. async updateFunRela(id, data) {
  481. const result = await this.db.update(this.tableName, {
  482. id: id, fun_rela: JSON.stringify({
  483. banOver: data.banOver, hintOver: data.hintOver, banMinusChangeBills: data.banMinusChangeBills,
  484. imType: data.imType, needGcl: data.needGcl, minusNoValue: data.minusNoValue,
  485. lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol,
  486. }),
  487. });
  488. return result.affectedRows === 1;
  489. }
  490. getFunSet(fun_set = null) {
  491. const result = fun_set ? JSON.parse(fun_set) : {};
  492. this.ctx.helper._.defaults(result, defaultFunSet);
  493. return result;
  494. }
  495. async updateFunSet(id, funSet) {
  496. const result = await this.db.update(this.tableName, {
  497. id, fun_set: JSON.stringify(funSet),
  498. });
  499. return result.affectedRows === 1;
  500. }
  501. }
  502. return SubProject;
  503. };