issues.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. const express = require('express');
  2. const { db } = require('../database/init');
  3. const router = express.Router();
  4. // 取得所有問題
  5. router.get('/', (req, res) => {
  6. const { project_id, status, priority, page = 1, limit = 10 } = req.query;
  7. let query = `
  8. SELECT i.*, p.name as project_name
  9. FROM issues i
  10. LEFT JOIN projects p ON i.project_id = p.id
  11. WHERE 1=1
  12. `;
  13. const params = [];
  14. if (project_id) {
  15. query += ' AND i.project_id = ?';
  16. params.push(project_id);
  17. }
  18. if (status) {
  19. query += ' AND i.status = ?';
  20. params.push(status);
  21. }
  22. if (priority) {
  23. query += ' AND i.priority = ?';
  24. params.push(priority);
  25. }
  26. query += ' ORDER BY i.created_at DESC';
  27. // 分頁
  28. const offset = (page - 1) * limit;
  29. query += ' LIMIT ? OFFSET ?';
  30. params.push(parseInt(limit), offset);
  31. db.all(query, params, (err, rows) => {
  32. if (err) {
  33. res.status(500).json({ success: false, message: '查詢失敗', error: err.message });
  34. } else {
  35. // 取得總數
  36. let countQuery = 'SELECT COUNT(*) as total FROM issues i WHERE 1=1';
  37. const countParams = [];
  38. if (project_id) {
  39. countQuery += ' AND i.project_id = ?';
  40. countParams.push(project_id);
  41. }
  42. if (status) {
  43. countQuery += ' AND i.status = ?';
  44. countParams.push(status);
  45. }
  46. if (priority) {
  47. countQuery += ' AND i.priority = ?';
  48. countParams.push(priority);
  49. }
  50. db.get(countQuery, countParams, (err, countResult) => {
  51. if (err) {
  52. res.status(500).json({ success: false, message: '查詢總數失敗', error: err.message });
  53. } else {
  54. res.json({
  55. success: true,
  56. data: rows,
  57. pagination: {
  58. page: parseInt(page),
  59. limit: parseInt(limit),
  60. total: countResult.total,
  61. pages: Math.ceil(countResult.total / limit)
  62. }
  63. });
  64. }
  65. });
  66. }
  67. });
  68. });
  69. // 取得單一問題
  70. router.get('/:id', (req, res) => {
  71. const { id } = req.params;
  72. const query = `
  73. SELECT i.*, p.name as project_name
  74. FROM issues i
  75. LEFT JOIN projects p ON i.project_id = p.id
  76. WHERE i.id = ?
  77. `;
  78. db.get(query, [id], (err, row) => {
  79. if (err) {
  80. res.status(500).json({ success: false, message: '查詢失敗', error: err.message });
  81. } else if (!row) {
  82. res.status(404).json({ success: false, message: '問題不存在' });
  83. } else {
  84. // 取得評論
  85. db.all('SELECT * FROM issue_comments WHERE issue_id = ? ORDER BY created_at ASC', [id], (err, comments) => {
  86. if (err) {
  87. res.status(500).json({ success: false, message: '查詢評論失敗', error: err.message });
  88. } else {
  89. res.json({
  90. success: true,
  91. data: { ...row, comments }
  92. });
  93. }
  94. });
  95. }
  96. });
  97. });
  98. // 建立新問題
  99. router.post('/', (req, res) => {
  100. const { project_id, title, description, priority, assignee, reporter } = req.body;
  101. if (!title) {
  102. return res.status(400).json({ success: false, message: '標題為必填欄位' });
  103. }
  104. const query = `
  105. INSERT INTO issues (project_id, title, description, priority, assignee, reporter)
  106. VALUES (?, ?, ?, ?, ?, ?)
  107. `;
  108. db.run(query, [project_id, title, description, priority || 'medium', assignee, reporter], function(err) {
  109. if (err) {
  110. res.status(500).json({ success: false, message: '建立失敗', error: err.message });
  111. } else {
  112. res.status(201).json({
  113. success: true,
  114. message: '問題建立成功',
  115. data: { id: this.lastID }
  116. });
  117. }
  118. });
  119. });
  120. // 更新問題
  121. router.put('/:id', (req, res) => {
  122. const { id } = req.params;
  123. const { title, description, status, priority, assignee } = req.body;
  124. const query = `
  125. UPDATE issues
  126. SET title = COALESCE(?, title),
  127. description = COALESCE(?, description),
  128. status = COALESCE(?, status),
  129. priority = COALESCE(?, priority),
  130. assignee = COALESCE(?, assignee),
  131. updated_at = CURRENT_TIMESTAMP
  132. WHERE id = ?
  133. `;
  134. db.run(query, [title, description, status, priority, assignee, id], function(err) {
  135. if (err) {
  136. res.status(500).json({ success: false, message: '更新失敗', error: err.message });
  137. } else if (this.changes === 0) {
  138. res.status(404).json({ success: false, message: '問題不存在' });
  139. } else {
  140. res.json({ success: true, message: '問題更新成功' });
  141. }
  142. });
  143. });
  144. // 刪除問題
  145. router.delete('/:id', (req, res) => {
  146. const { id } = req.params;
  147. db.run('DELETE FROM issues WHERE id = ?', [id], function(err) {
  148. if (err) {
  149. res.status(500).json({ success: false, message: '刪除失敗', error: err.message });
  150. } else if (this.changes === 0) {
  151. res.status(404).json({ success: false, message: '問題不存在' });
  152. } else {
  153. res.json({ success: true, message: '問題刪除成功' });
  154. }
  155. });
  156. });
  157. // 新增評論
  158. router.post('/:id/comments', (req, res) => {
  159. const { id } = req.params;
  160. const { author, content } = req.body;
  161. if (!author || !content) {
  162. return res.status(400).json({ success: false, message: '作者和內容為必填欄位' });
  163. }
  164. const query = 'INSERT INTO issue_comments (issue_id, author, content) VALUES (?, ?, ?)';
  165. db.run(query, [id, author, content], function(err) {
  166. if (err) {
  167. res.status(500).json({ success: false, message: '新增評論失敗', error: err.message });
  168. } else {
  169. res.status(201).json({
  170. success: true,
  171. message: '評論新增成功',
  172. data: { id: this.lastID }
  173. });
  174. }
  175. });
  176. });
  177. module.exports = router;