Loggest thy stuff
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.

340 lines
8.2 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package mysql
  2. import (
  3. "context"
  4. "database/sql"
  5. "git.aiterp.net/stufflog3/stufflog3-api/internal/database/mysql/mysqlcore"
  6. "git.aiterp.net/stufflog3/stufflog3-api/internal/models"
  7. "git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors"
  8. "golang.org/x/sync/errgroup"
  9. )
  10. type projectRepository struct {
  11. db *sql.DB
  12. q *mysqlcore.Queries
  13. items *itemRepository
  14. scopeID int
  15. }
  16. func (r *projectRepository) Find(ctx context.Context, id int) (*models.Project, error) {
  17. row, err := r.q.GetProject(ctx, id)
  18. if err == sql.ErrNoRows || row.ScopeID != r.scopeID {
  19. return nil, slerrors.NotFound("Project")
  20. } else if err != nil {
  21. return nil, err
  22. }
  23. project := models.Project{
  24. ProjectEntry: models.ProjectEntry{
  25. ID: row.ID,
  26. OwnerID: row.AuthorID,
  27. CreatedTime: row.CreatedTime,
  28. Name: row.Name,
  29. Status: models.Status(row.Status),
  30. },
  31. Description: row.Description,
  32. Requirements: []models.ProjectRequirement{},
  33. }
  34. reqs, err := r.q.ListProjectRequirementsByProjectID(ctx, id)
  35. if err != nil && err != sql.ErrNoRows {
  36. return nil, err
  37. }
  38. itemRows, err := r.q.ListItemsByProject(ctx, id)
  39. if err != nil && err != sql.ErrNoRows {
  40. return nil, err
  41. }
  42. items := make([]models.Item, 0, len(itemRows))
  43. for _, itemRow := range itemRows {
  44. item := r.items.resToItem(mysqlcore.ListItemsAcquiredBetweenRow(itemRow))
  45. items = append(items, item)
  46. }
  47. err = r.items.fillStats(ctx, items)
  48. if err != nil {
  49. return nil, err
  50. }
  51. eg, ctx := errgroup.WithContext(ctx)
  52. for i := range reqs {
  53. project.Requirements = append(project.Requirements, models.ProjectRequirement{
  54. ID: reqs[i].ID,
  55. Name: reqs[i].Name,
  56. Description: reqs[i].Description,
  57. Status: models.Status(reqs[i].Status),
  58. Stats: []models.StatProgressEntry{},
  59. Items: nil,
  60. })
  61. requirement := &project.Requirements[len(project.Requirements)-1]
  62. for _, item := range items {
  63. if *item.ProjectRequirementID == requirement.ID {
  64. requirement.Items = append(requirement.Items, item)
  65. }
  66. }
  67. eg.Go(func() error {
  68. stats, err := r.q.ListProjectRequirementStats(ctx, requirement.ID)
  69. if err != nil && err != sql.ErrNoRows {
  70. return err
  71. }
  72. for _, statRow := range stats {
  73. stat := models.StatProgressEntry{
  74. StatEntry: models.StatEntry{
  75. ID: statRow.ID,
  76. Name: statRow.Name,
  77. Weight: statRow.Weight,
  78. },
  79. Acquired: 0,
  80. Required: int(statRow.Required.Int32),
  81. }
  82. for _, item := range requirement.Items {
  83. for _, stat2 := range item.Stats {
  84. if stat2.ID == stat.ID {
  85. stat.Acquired += stat2.Acquired
  86. }
  87. }
  88. }
  89. requirement.Stats = append(requirement.Stats, stat)
  90. }
  91. return nil
  92. })
  93. }
  94. err = eg.Wait()
  95. if err != nil {
  96. return nil, err
  97. }
  98. return &project, nil
  99. }
  100. func (r *projectRepository) List(ctx context.Context) ([]models.ProjectEntry, error) {
  101. rows, err := r.q.ListProjectEntries(ctx, r.scopeID)
  102. if err != nil && err != sql.ErrNoRows {
  103. return nil, err
  104. }
  105. projects := make([]models.ProjectEntry, 0, len(rows))
  106. for _, row := range rows {
  107. projects = append(projects, models.ProjectEntry{
  108. ID: row.ID,
  109. OwnerID: row.AuthorID,
  110. CreatedTime: row.CreatedTime,
  111. Name: row.Name,
  112. Status: models.Status(row.Status),
  113. })
  114. }
  115. return projects, nil
  116. }
  117. func (r *projectRepository) Create(ctx context.Context, project models.Project) (*models.Project, error) {
  118. res, err := r.q.InsertProject(ctx, mysqlcore.InsertProjectParams{
  119. ScopeID: r.scopeID,
  120. AuthorID: project.OwnerID,
  121. Name: project.Name,
  122. Status: int(project.Status),
  123. Description: project.Description,
  124. CreatedTime: project.CreatedTime,
  125. })
  126. if err != nil {
  127. return nil, err
  128. }
  129. id, err := res.LastInsertId()
  130. if err != nil {
  131. return nil, err
  132. }
  133. return r.Find(ctx, int(id))
  134. }
  135. func (r *projectRepository) Update(ctx context.Context, project models.Project, update models.ProjectUpdate) (*models.Project, error) {
  136. project.Update(update)
  137. err := r.q.UpdateProject(ctx, mysqlcore.UpdateProjectParams{
  138. Name: project.Name,
  139. Status: int(project.Status),
  140. Description: project.Description,
  141. ID: project.ID,
  142. })
  143. if err != nil {
  144. return nil, err
  145. }
  146. return r.Find(ctx, project.ID)
  147. }
  148. func (r *projectRepository) Delete(ctx context.Context, project models.ProjectEntry, deleteItems bool) error {
  149. tx, err := r.db.BeginTx(ctx, nil)
  150. if err != nil {
  151. return err
  152. }
  153. defer tx.Rollback()
  154. q := r.q.WithTx(tx)
  155. err = q.DeleteProject(ctx, project.ID)
  156. if err != nil {
  157. return err
  158. }
  159. if deleteItems {
  160. reqs, err := q.ListProjectRequirementsByProjectID(ctx, project.ID)
  161. if err != nil {
  162. return err
  163. }
  164. for _, req := range reqs {
  165. err = q.DeleteItemForRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(req.ID)})
  166. if err != nil {
  167. return err
  168. }
  169. err = q.DeleteAllProjectRequirementStats(ctx, req.ID)
  170. if err != nil {
  171. return err
  172. }
  173. }
  174. } else {
  175. err = q.ClearItemProjectRequirementByProjectID(ctx, project.ID)
  176. if err != nil {
  177. return err
  178. }
  179. }
  180. return tx.Commit()
  181. }
  182. func (r *projectRepository) AddRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement) (*models.Project, error) {
  183. tx, err := r.db.BeginTx(ctx, nil)
  184. if err != nil {
  185. return nil, err
  186. }
  187. defer tx.Rollback()
  188. q := r.q.WithTx(tx)
  189. res, err := q.InsertProjectRequirement(ctx, mysqlcore.InsertProjectRequirementParams{
  190. ScopeID: r.scopeID,
  191. ProjectID: project.ID,
  192. Name: requirement.Name,
  193. Status: int(requirement.Status),
  194. Description: requirement.Description,
  195. })
  196. if err != nil {
  197. return nil, err
  198. }
  199. id, err := res.LastInsertId()
  200. if err != nil {
  201. return nil, err
  202. }
  203. for _, stat := range requirement.Stats {
  204. err = q.ReplaceProjectRequirementStat(ctx, mysqlcore.ReplaceProjectRequirementStatParams{
  205. ProjectRequirementID: int(id),
  206. StatID: stat.ID,
  207. Required: stat.Required,
  208. })
  209. if err != nil {
  210. return nil, err
  211. }
  212. }
  213. err = tx.Commit()
  214. if err != nil {
  215. return nil, err
  216. }
  217. return r.Find(ctx, project.ID)
  218. }
  219. func (r *projectRepository) UpdateRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, update models.ProjectRequirementUpdate) (*models.Project, error) {
  220. tx, err := r.db.BeginTx(ctx, nil)
  221. if err != nil {
  222. return nil, err
  223. }
  224. defer tx.Rollback()
  225. q := r.q.WithTx(tx)
  226. requirement.Update(update)
  227. err = q.UpdateProjectRequirement(ctx, mysqlcore.UpdateProjectRequirementParams{
  228. Name: requirement.Name,
  229. Status: int(requirement.Status),
  230. Description: requirement.Description,
  231. ID: requirement.ID,
  232. })
  233. if err != nil {
  234. return nil, err
  235. }
  236. for _, stat := range requirement.Stats {
  237. if stat.Required < 0 {
  238. err = q.DeleteProjectRequirementStat(ctx, mysqlcore.DeleteProjectRequirementStatParams{
  239. ProjectRequirementID: requirement.ID,
  240. StatID: stat.ID,
  241. })
  242. } else {
  243. err = q.ReplaceProjectRequirementStat(ctx, mysqlcore.ReplaceProjectRequirementStatParams{
  244. ProjectRequirementID: requirement.ID,
  245. StatID: stat.ID,
  246. Required: stat.Required,
  247. })
  248. }
  249. if err != nil {
  250. return nil, err
  251. }
  252. }
  253. err = tx.Commit()
  254. if err != nil {
  255. return nil, err
  256. }
  257. return r.Find(ctx, project.ID)
  258. }
  259. func (r *projectRepository) DeleteRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, deleteItems bool) (*models.Project, error) {
  260. tx, err := r.db.BeginTx(ctx, nil)
  261. if err != nil {
  262. return nil, err
  263. }
  264. defer tx.Rollback()
  265. q := r.q.WithTx(tx)
  266. err = q.DeleteProjectRequirement(ctx, requirement.ID)
  267. if err != nil {
  268. if err == sql.ErrNoRows {
  269. return nil, slerrors.NotFound("Project requirement")
  270. }
  271. return nil, err
  272. }
  273. if deleteItems {
  274. err = q.DeleteItemForRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(requirement.ID)})
  275. if err != nil {
  276. return nil, err
  277. }
  278. } else {
  279. err = q.ClearItemProjectRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(requirement.ID)})
  280. if err != nil {
  281. return nil, err
  282. }
  283. err = q.DeleteAllProjectRequirementStats(ctx, requirement.ID)
  284. if err != nil {
  285. return nil, err
  286. }
  287. }
  288. err = tx.Commit()
  289. if err != nil {
  290. return nil, err
  291. }
  292. return r.Find(ctx, project.ID)
  293. }