From fe533b33511cb2e3b84075ded7120e35baabd656 Mon Sep 17 00:00:00 2001 From: zwl <1633720889@qq.com> Date: Mon, 16 Dec 2024 00:43:58 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20=E9=9D=A2=E7=BB=8F=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/resume/internal/web/project.go | 4 +- internal/review/internal/domain/review.go | 27 + internal/review/internal/errs/code.go | 10 + .../internal/integration/handler_test.go | 791 ++++++++++++++++++ .../internal/integration/startup/wire.go | 33 + .../internal/integration/startup/wire_gen.go | 42 + .../review/internal/repository/dao/init.go | 10 + .../review/internal/repository/dao/review.go | 144 ++++ .../review/internal/repository/dao/type.go | 16 + internal/review/internal/repository/review.go | 108 +++ internal/review/internal/service/review.go | 71 ++ internal/review/internal/web/admin_handler.go | 78 ++ internal/review/internal/web/handler.go | 55 ++ internal/review/internal/web/result.go | 13 + internal/review/internal/web/vo.go | 57 ++ internal/review/module.go | 10 + internal/review/wire.go | 32 + internal/review/wire_gen.go | 41 + ioc/admin.go | 4 + ioc/gin.go | 6 +- ioc/wire.go | 3 + ioc/wire_gen.go | 10 +- 22 files changed, 1559 insertions(+), 6 deletions(-) create mode 100644 internal/review/internal/domain/review.go create mode 100644 internal/review/internal/errs/code.go create mode 100644 internal/review/internal/integration/handler_test.go create mode 100644 internal/review/internal/integration/startup/wire.go create mode 100644 internal/review/internal/integration/startup/wire_gen.go create mode 100644 internal/review/internal/repository/dao/init.go create mode 100644 internal/review/internal/repository/dao/review.go create mode 100644 internal/review/internal/repository/dao/type.go create mode 100644 internal/review/internal/repository/review.go create mode 100644 internal/review/internal/service/review.go create mode 100644 internal/review/internal/web/admin_handler.go create mode 100644 internal/review/internal/web/handler.go create mode 100644 internal/review/internal/web/result.go create mode 100644 internal/review/internal/web/vo.go create mode 100644 internal/review/module.go create mode 100644 internal/review/wire.go create mode 100644 internal/review/wire_gen.go diff --git a/internal/resume/internal/web/project.go b/internal/resume/internal/web/project.go index e840520..ccf7be2 100644 --- a/internal/resume/internal/web/project.go +++ b/internal/resume/internal/web/project.go @@ -48,7 +48,7 @@ func (h *ProjectHandler) DeleteContribution(ctx *ginx.Context, item IDItem) (gin } func (h *ProjectHandler) DeleteDifficulty(ctx *ginx.Context, item IDItem) (ginx.Result, error) { - err := h.svc.DeleteDifficulty(ctx.Request.Context(), item.ID) + err := h.svc.DeleteDifficulty(ctx, item.ID) if err != nil { return systemErrorResult, err } @@ -86,7 +86,7 @@ func (h *ProjectHandler) DeleteProject(ctx *ginx.Context, req IDItem, sess sessi func (h *ProjectHandler) ProjectInfo(ctx *ginx.Context, req IDItem, sess session.Session) (ginx.Result, error) { uid := sess.Claims().Uid - pro, err := h.svc.ProjectInfo(ctx.Request.Context(), req.ID) + pro, err := h.svc.ProjectInfo(ctx, req.ID) if err != nil { return systemErrorResult, err } diff --git a/internal/review/internal/domain/review.go b/internal/review/internal/domain/review.go new file mode 100644 index 0000000..1750dda --- /dev/null +++ b/internal/review/internal/domain/review.go @@ -0,0 +1,27 @@ +package domain + +type Review struct { + ID int64 + Uid int64 + JD string + JDAnalysis string + Questions string + QuestionAnalysis string + Resume string + Status ReviewStatus + Utime int64 +} +type ReviewStatus uint8 + +func (s ReviewStatus) ToUint8() uint8 { + return uint8(s) +} + +const ( + // UnknownStatus 未知 + UnknownStatus ReviewStatus = 0 + // UnPublishedStatus 未发布 + UnPublishedStatus ReviewStatus = 1 + // PublishedStatus 发布 + PublishedStatus ReviewStatus = 2 +) diff --git a/internal/review/internal/errs/code.go b/internal/review/internal/errs/code.go new file mode 100644 index 0000000..79e6432 --- /dev/null +++ b/internal/review/internal/errs/code.go @@ -0,0 +1,10 @@ +package errs + +var ( + SystemError = ErrorCode{Code: 516001, Msg: "系统错误"} +) + +type ErrorCode struct { + Code int + Msg string +} diff --git a/internal/review/internal/integration/handler_test.go b/internal/review/internal/integration/handler_test.go new file mode 100644 index 0000000..06ea6d3 --- /dev/null +++ b/internal/review/internal/integration/handler_test.go @@ -0,0 +1,791 @@ +package integration + +import ( + "context" + "fmt" + "net/http" + "strconv" + "testing" + "time" + + "github.com/ecodeclub/ekit/iox" + "github.com/ecodeclub/ginx/session" + "github.com/ecodeclub/webook/internal/review/internal/domain" + "github.com/ecodeclub/webook/internal/review/internal/integration/startup" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" + "github.com/ecodeclub/webook/internal/review/internal/web" + "github.com/ecodeclub/webook/internal/test" + testioc "github.com/ecodeclub/webook/internal/test/ioc" + "github.com/ego-component/egorm" + "github.com/gin-gonic/gin" + "github.com/gotomicro/ego/core/econf" + "github.com/gotomicro/ego/server/egin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +const uid = 123 + +type TestSuite struct { + suite.Suite + db *egorm.Component + server *egin.Component + reviewDao dao.ReviewDAO +} + +func (a *TestSuite) SetupSuite() { + db := testioc.InitDB() + mou := startup.InitModule(db) + econf.Set("server", map[string]any{"contextTimeout": "1s"}) + server := egin.Load("server").Build() + server.Use(func(ctx *gin.Context) { + ctx.Set("_session", session.NewMemorySession(session.Claims{ + Uid: uid, + Data: map[string]string{ + "creator": "true", + "memberDDL": strconv.FormatInt(time.Now().Add(time.Hour).UnixMilli(), 10), + }, + })) + }) + mou.Hdl.MemberRoutes(server.Engine) + mou.Hdl.PublicRoutes(server.Engine) + mou.AdminHdl.PrivateRoutes(server.Engine) + reviewDao := dao.NewReviewDAO(db) + a.db = db + a.server = server + a.reviewDao = reviewDao +} + +func (s *TestSuite) TearDownTest() { + err := s.db.Exec("TRUNCATE TABLE `reviews`").Error + require.NoError(s.T(), err) + err = s.db.Exec("TRUNCATE TABLE `publish_reviews`").Error + require.NoError(s.T(), err) +} + +func (s *TestSuite) TestSave() { + testCases := []struct { + name string + before func(t *testing.T) + after func(t *testing.T) + req web.ReviewSaveReq + wantCode int + wantResp test.Result[int64] + }{ + { + name: "新建面试评测", + before: func(t *testing.T) { + }, + after: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + review, err := s.reviewDao.Get(ctx, 1) + require.NoError(t, err) + s.assertReview(t, dao.Review{ + ID: 1, + Uid: uid, + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + Status: domain.UnPublishedStatus.ToUint8(), // 未发布状态 + }, review) + }, + req: web.ReviewSaveReq{ + Review: web.Review{ + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + }, + }, + wantCode: 200, + wantResp: test.Result[int64]{ + Data: 1, + }, + }, + { + name: "更新面试评测", + before: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + _, err := s.reviewDao.Save(ctx, dao.Review{ + ID: 2, + Uid: uid, + JD: "旧的JD", + JDAnalysis: "旧的分析", + Questions: "旧的问题", + QuestionAnalysis: "旧的分析", + Resume: "旧的简历", + Status: domain.UnPublishedStatus.ToUint8(), + }) + require.NoError(t, err) + }, + after: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + review, err := s.reviewDao.Get(ctx, 2) + require.NoError(t, err) + s.assertReview(t, dao.Review{ + ID: 2, + Uid: uid, + JD: "新的JD", + JDAnalysis: "新的分析", + Questions: "新的问题", + QuestionAnalysis: "新的分析", + Resume: "新的简历", + Status: domain.UnPublishedStatus.ToUint8(), + }, review) + }, + req: web.ReviewSaveReq{ + Review: web.Review{ + ID: 2, + JD: "新的JD", + JDAnalysis: "新的分析", + Questions: "新的问题", + QuestionAnalysis: "新的分析", + Resume: "新的简历", + }, + }, + wantCode: 200, + wantResp: test.Result[int64]{ + Data: 2, + }, + }, + } + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + tc.before(t) + req, err := http.NewRequest(http.MethodPost, + "/review/save", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + recorder := test.NewJSONResponseRecorder[int64]() + s.server.ServeHTTP(recorder, req) + require.Equal(t, tc.wantCode, recorder.Code) + assert.Equal(t, tc.wantResp, recorder.MustScan()) + tc.after(t) + }) + } +} + +func (s *TestSuite) TestPublish() { + testCases := []struct { + name string + before func(t *testing.T) + after func(t *testing.T) + req web.ReviewSaveReq + wantCode int + wantResp test.Result[int64] + }{ + { + name: "新建并发布", + before: func(t *testing.T) { + }, + after: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + // 检查原始表中的数据 + review, err := s.reviewDao.Get(ctx, 1) + require.NoError(t, err) + wantReview := dao.Review{ + ID: 1, + Uid: uid, + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + Status: domain.PublishedStatus.ToUint8(), // 已发布状态 + } + s.assertReview(t, wantReview, review) + + // 检查发布表中的数据 + pubReview, err := s.reviewDao.GetPublishReview(ctx, 1) + require.NoError(t, err) + s.assertReview(t, dao.Review(wantReview), dao.Review(pubReview)) + }, + req: web.ReviewSaveReq{ + Review: web.Review{ + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + }, + }, + wantCode: 200, + wantResp: test.Result[int64]{ + Data: 1, + }, + }, + { + name: "更新并发布", + before: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + // 先创建一条记录 + _, err := s.reviewDao.Save(ctx, dao.Review{ + ID: 2, + Uid: uid, + JD: "旧的JD", + JDAnalysis: "旧的分析", + Questions: "旧的问题", + QuestionAnalysis: "旧的分析", + Resume: "旧的简历", + Status: 1, + Ctime: 123, + Utime: 234, + }) + require.NoError(t, err) + }, + after: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + // 检查原始表 + review, err := s.reviewDao.Get(ctx, 2) + require.NoError(t, err) + + wantReview := dao.Review{ + ID: 2, + Uid: uid, + JD: "新的JD", + JDAnalysis: "新的分析", + Questions: "新的问题", + QuestionAnalysis: "新的分析", + Resume: "新的简历", + Status: 2, // 已发布状态 + } + s.assertReview(t, wantReview, review) + + // 检查发布表 + pubReview, err := s.reviewDao.GetPublishReview(ctx, 2) + require.NoError(t, err) + s.assertReview(t, dao.Review(wantReview), dao.Review(pubReview)) + }, + req: web.ReviewSaveReq{ + Review: web.Review{ + ID: 2, + JD: "新的JD", + JDAnalysis: "新的分析", + Questions: "新的问题", + QuestionAnalysis: "新的分析", + Resume: "新的简历", + }, + }, + wantCode: 200, + wantResp: test.Result[int64]{ + Data: 2, + }, + }, + { + name: "发布表已有记录时更新发布", + before: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + // 创建原始记录 + oldReview := dao.Review{ + ID: 3, + Uid: uid, + JD: "旧的JD", + JDAnalysis: "旧的分析", + Questions: "旧的问题", + QuestionAnalysis: "旧的分析", + Resume: "旧的简历", + Status: domain.UnPublishedStatus.ToUint8(), + } + _, err := s.reviewDao.Save(ctx, oldReview) + require.NoError(t, err) + + // 创建发布表记录 + _, err = s.reviewDao.Sync(ctx, oldReview) + require.NoError(t, err) + }, + after: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + wantReview := dao.Review{ + ID: 3, + Uid: uid, + JD: "最新JD", + JDAnalysis: "最新分析", + Questions: "最新问题", + QuestionAnalysis: "最新分析", + Resume: "最新简历", + Status: domain.PublishedStatus.ToUint8(), + } + + // 检查原始表 + review, err := s.reviewDao.Get(ctx, 3) + require.NoError(t, err) + s.assertReview(t, wantReview, review) + + // 检查发布表 + pubReview, err := s.reviewDao.GetPublishReview(ctx, 3) + require.NoError(t, err) + s.assertReview(t, dao.Review(wantReview), dao.Review(pubReview)) + }, + req: web.ReviewSaveReq{ + Review: web.Review{ + ID: 3, + JD: "最新JD", + JDAnalysis: "最新分析", + Questions: "最新问题", + QuestionAnalysis: "最新分析", + Resume: "最新简历", + }, + }, + wantCode: 200, + wantResp: test.Result[int64]{ + Data: 3, + }, + }, + } + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + // 运行前置操作 + tc.before(t) + + // 构造请求 + req, err := http.NewRequest(http.MethodPost, + "/review/publish", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + + // 发送请求并记录响应 + recorder := test.NewJSONResponseRecorder[int64]() + s.server.ServeHTTP(recorder, req) + + // 验证响应 + require.Equal(t, tc.wantCode, recorder.Code) + assert.Equal(t, tc.wantResp, recorder.MustScan()) + + // 运行后置检查 + tc.after(t) + + // 清理数据 + err = s.db.Exec("TRUNCATE table `reviews`").Error + require.NoError(t, err) + err = s.db.Exec("TRUNCATE table `publish_reviews`").Error + require.NoError(t, err) + }) + } +} + +func (s *TestSuite) TestDetail() { + testCases := []struct { + name string + before func(t *testing.T) + after func(t *testing.T) + req web.DetailReq + wantCode int + wantResp test.Result[web.Review] + }{ + { + name: "查询存在的记录", + before: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + _, err := s.reviewDao.Save(ctx, dao.Review{ + ID: 1, + Uid: uid, + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + Status: domain.UnPublishedStatus.ToUint8(), + }) + require.NoError(t, err) + }, + after: func(t *testing.T) { + }, + req: web.DetailReq{ + ID: 1, + }, + wantCode: 200, + wantResp: test.Result[web.Review]{ + Data: web.Review{ + ID: 1, + JD: "测试JD", + JDAnalysis: "JD分析", + Questions: "面试问题", + QuestionAnalysis: "问题分析", + Resume: "简历内容", + Status: domain.UnPublishedStatus.ToUint8(), + }, + }, + }, + { + name: "查询不存在的记录", + before: func(t *testing.T) { + }, + after: func(t *testing.T) { + }, + req: web.DetailReq{ + ID: 999, + }, + wantCode: 500, + wantResp: test.Result[web.Review]{ + Code: 516001, + Msg: "系统错误", + }, + }, + } + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + // 运行前置操作 + tc.before(t) + + // 构造请求 + req, err := http.NewRequest(http.MethodPost, + "/review/detail", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + + // 发送请求并记录响应 + recorder := test.NewJSONResponseRecorder[web.Review]() + s.server.ServeHTTP(recorder, req) + + // 验证响应 + require.Equal(t, tc.wantCode, recorder.Code) + if tc.wantCode != 200 { + return + } + resp := recorder.MustScan() + require.True(t, resp.Data.Utime != 0) + resp.Data.Utime = 0 + assert.Equal(t, tc.wantResp, resp) + + // 运行后置检查 + tc.after(t) + + // 清理数据 + err = s.db.Exec("TRUNCATE table `reviews`").Error + require.NoError(t, err) + err = s.db.Exec("TRUNCATE table `publish_reviews`").Error + require.NoError(t, err) + }) + } +} + +func (s *TestSuite) TestList() { + data := make([]dao.Review, 0, 100) + for idx := 0; idx < 100; idx++ { + data = append(data, dao.Review{ + Uid: uid, + JD: fmt.Sprintf("这是JD %d", idx), + JDAnalysis: fmt.Sprintf("这是JD分析 %d", idx), + Questions: fmt.Sprintf("这是面试问题 %d", idx), + QuestionAnalysis: fmt.Sprintf("这是问题分析 %d", idx), + Resume: fmt.Sprintf("这是简历 %d", idx), + Status: domain.UnPublishedStatus.ToUint8(), + Utime: 123, + }) + } + err := s.db.Create(&data).Error + require.NoError(s.T(), err) + testCases := []struct { + name string + req web.Page + wantCode int + wantResp test.Result[web.ReviewListResp] + }{ + { + name: "获取第一页", + req: web.Page{ + Limit: 2, + Offset: 0, + }, + wantCode: 200, + wantResp: test.Result[web.ReviewListResp]{ + Data: web.ReviewListResp{ + Total: 100, + List: []web.Review{ + { + ID: 100, + JD: "这是JD 99", + JDAnalysis: "这是JD分析 99", + Questions: "这是面试问题 99", + QuestionAnalysis: "这是问题分析 99", + Resume: "这是简历 99", + Status: domain.UnPublishedStatus.ToUint8(), + Utime: 123, + }, + { + ID: 99, + JD: "这是JD 98", + JDAnalysis: "这是JD分析 98", + Questions: "这是面试问题 98", + QuestionAnalysis: "这是问题分析 98", + Resume: "这是简历 98", + Status: domain.UnPublishedStatus.ToUint8(), + Utime: 123, + }, + }, + }, + }, + }, + { + name: "获取最后一页", + req: web.Page{ + Limit: 2, + Offset: 99, + }, + wantCode: 200, + wantResp: test.Result[web.ReviewListResp]{ + Data: web.ReviewListResp{ + Total: 100, + List: []web.Review{ + { + ID: 1, + JD: "这是JD 0", + JDAnalysis: "这是JD分析 0", + Questions: "这是面试问题 0", + QuestionAnalysis: "这是问题分析 0", + Resume: "这是简历 0", + Status: domain.UnPublishedStatus.ToUint8(), + Utime: 123, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + tc := tc + s.T().Run(tc.name, func(t *testing.T) { + req, err := http.NewRequest(http.MethodPost, + "/review/list", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + recorder := test.NewJSONResponseRecorder[web.ReviewListResp]() + s.server.ServeHTTP(recorder, req) + require.Equal(t, tc.wantCode, recorder.Code) + assert.Equal(t, tc.wantResp, recorder.MustScan()) + }) + } +} + +func (s *TestSuite) TestPubList() { + // 插入一百条测试数据到原始表和发布表 + data := make([]dao.Review, 0, 100) + pubData := make([]dao.PublishReview, 0, 50) // 只发布一半的数据 + + for idx := 1; idx <= 100; idx++ { + review := dao.Review{ + ID: int64(idx), + Uid: uid, + JD: fmt.Sprintf("这是JD %d", idx), + JDAnalysis: fmt.Sprintf("这是JD分析 %d", idx), + Questions: fmt.Sprintf("这是面试问题 %d", idx), + QuestionAnalysis: fmt.Sprintf("这是问题分析 %d", idx), + Resume: fmt.Sprintf("这是简历 %d", idx), + Status: domain.UnPublishedStatus.ToUint8(), + Utime: 123, + } + data = append(data, review) + + // 偶数ID的记录设为已发布 + if idx%2 == 0 { + review.Status = domain.PublishedStatus.ToUint8() + pubData = append(pubData, dao.PublishReview(review)) + } + } + + err := s.db.Create(&data).Error + require.NoError(s.T(), err) + err = s.db.Create(&pubData).Error + require.NoError(s.T(), err) + + testCases := []struct { + name string + req web.Page + wantCode int + wantResp test.Result[web.ReviewListResp] + }{ + { + name: "获取第一页已发布记录", + req: web.Page{ + Limit: 2, + Offset: 0, + }, + wantCode: 200, + wantResp: test.Result[web.ReviewListResp]{ + Data: web.ReviewListResp{ + Total: 0, // 只有50条已发布的记录 + List: []web.Review{ + { + ID: 100, + JD: "这是JD 100", + JDAnalysis: "这是JD分析 100", + Questions: "这是面试问题 100", + QuestionAnalysis: "这是问题分析 100", + Resume: "这是简历 100", + Status: domain.PublishedStatus.ToUint8(), + Utime: 123, + }, + { + ID: 98, + JD: "这是JD 98", + JDAnalysis: "这是JD分析 98", + Questions: "这是面试问题 98", + QuestionAnalysis: "这是问题分析 98", + Resume: "这是简历 98", + Status: domain.PublishedStatus.ToUint8(), + Utime: 123, + }, + }, + }, + }, + }, + { + name: "获取最后一页已发布记录", + req: web.Page{ + Limit: 2, + Offset: 48, + }, + wantCode: 200, + wantResp: test.Result[web.ReviewListResp]{ + Data: web.ReviewListResp{ + Total: 0, + List: []web.Review{ + { + ID: 4, + JD: "这是JD 4", + JDAnalysis: "这是JD分析 4", + Questions: "这是面试问题 4", + QuestionAnalysis: "这是问题分析 4", + Resume: "这是简历 4", + Status: domain.PublishedStatus.ToUint8(), + Utime: 123, + }, + { + ID: 2, + JD: "这是JD 2", + JDAnalysis: "这是JD分析 2", + Questions: "这是面试问题 2", + QuestionAnalysis: "这是问题分析 2", + Resume: "这是简历 2", + Status: domain.PublishedStatus.ToUint8(), + Utime: 123, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + tc := tc + s.T().Run(tc.name, func(t *testing.T) { + req, err := http.NewRequest(http.MethodPost, + "/review/pub/list", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + recorder := test.NewJSONResponseRecorder[web.ReviewListResp]() + s.server.ServeHTTP(recorder, req) + require.Equal(t, tc.wantCode, recorder.Code) + assert.Equal(t, tc.wantResp, recorder.MustScan()) + }) + } +} + +func (s *TestSuite) TestPubDetail() { + testCases := []struct { + name string + before func(t *testing.T) + req web.DetailReq + wantCode int + wantResp test.Result[web.Review] + }{ + { + name: "获取已发布的记录", + before: func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + // 创建原始记录 + review := dao.Review{ + ID: 1, + Uid: uid, + JD: "已发布的JD", + JDAnalysis: "已发布的JD分析", + Questions: "已发布的面试问题", + QuestionAnalysis: "已发布的问题分析", + Resume: "已发布的简历", + Status: domain.PublishedStatus.ToUint8(), + } + _, err := s.reviewDao.Save(ctx, review) + require.NoError(t, err) + + // 同步到发布表 + _, err = s.reviewDao.Sync(ctx, review) + require.NoError(t, err) + }, + req: web.DetailReq{ + ID: 1, + }, + wantCode: 200, + wantResp: test.Result[web.Review]{ + Data: web.Review{ + ID: 1, + JD: "已发布的JD", + JDAnalysis: "已发布的JD分析", + Questions: "已发布的面试问题", + QuestionAnalysis: "已发布的问题分析", + Resume: "已发布的简历", + Status: domain.PublishedStatus.ToUint8(), + }, + }, + }, + } + for _, tc := range testCases { + tc := tc + s.T().Run(tc.name, func(t *testing.T) { + // 运行前置操作 + tc.before(t) + + // 构造请求 + req, err := http.NewRequest(http.MethodPost, + "/review/pub/detail", iox.NewJSONReader(tc.req)) + req.Header.Set("content-type", "application/json") + require.NoError(t, err) + + // 发送请求并记录响应 + recorder := test.NewJSONResponseRecorder[web.Review]() + s.server.ServeHTTP(recorder, req) + + // 验证响应 + require.Equal(t, tc.wantCode, recorder.Code) + resp := recorder.MustScan() + assert.True(t, resp.Data.Utime != 0) + resp.Data.Utime = 0 + assert.Equal(t, tc.wantResp, resp) + + // 清理数据 + err = s.db.Exec("TRUNCATE table `reviews`").Error + require.NoError(t, err) + err = s.db.Exec("TRUNCATE table `publish_reviews`").Error + require.NoError(t, err) + }) + } +} + +// assertReview 比较两个 Review 对象,忽略时间字段 +func (s *TestSuite) assertReview(t *testing.T, expect dao.Review, actual dao.Review) { + require.True(s.T(), actual.Ctime != 0) + require.True(s.T(), actual.Utime != 0) + actual.Ctime = 0 + actual.Utime = 0 + + assert.Equal(t, expect, actual) +} + +func TestReviewHandler(t *testing.T) { + suite.Run(t, new(TestSuite)) +} diff --git a/internal/review/internal/integration/startup/wire.go b/internal/review/internal/integration/startup/wire.go new file mode 100644 index 0000000..5e41e73 --- /dev/null +++ b/internal/review/internal/integration/startup/wire.go @@ -0,0 +1,33 @@ +//go:build wireinject + +package startup + +import ( + "github.com/ecodeclub/webook/internal/review" + "github.com/ecodeclub/webook/internal/review/internal/repository" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/ecodeclub/webook/internal/review/internal/web" + "github.com/ego-component/egorm" + "github.com/google/wire" +) + +func InitModule(db *egorm.Component) *review.Module { + wire.Build( + initReviewDao, + repository.NewReviewRepo, + service.NewReviewSvc, + web.NewHandler, + web.NewAdminHandler, + wire.Struct(new(review.Module), "*"), + ) + return new(review.Module) +} + +func initReviewDao(db *egorm.Component) dao.ReviewDAO { + err := dao.InitTables(db) + if err != nil { + panic(err) + } + return dao.NewReviewDAO(db) +} diff --git a/internal/review/internal/integration/startup/wire_gen.go b/internal/review/internal/integration/startup/wire_gen.go new file mode 100644 index 0000000..95dc577 --- /dev/null +++ b/internal/review/internal/integration/startup/wire_gen.go @@ -0,0 +1,42 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package startup + +import ( + "github.com/ecodeclub/webook/internal/review" + "github.com/ecodeclub/webook/internal/review/internal/repository" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/ecodeclub/webook/internal/review/internal/web" + "github.com/ego-component/egorm" + "gorm.io/gorm" +) + +// Injectors from wire.go: + +func InitModule(db *gorm.DB) *review.Module { + reviewDAO := initReviewDao(db) + reviewRepo := repository.NewReviewRepo(reviewDAO) + reviewSvc := service.NewReviewSvc(reviewRepo) + handler := web.NewHandler(reviewSvc) + adminHandler := web.NewAdminHandler(reviewSvc) + module := &review.Module{ + Hdl: handler, + AdminHdl: adminHandler, + } + return module +} + +// wire.go: + +func initReviewDao(db *egorm.Component) dao.ReviewDAO { + err := dao.InitTables(db) + if err != nil { + panic(err) + } + return dao.NewReviewDAO(db) +} diff --git a/internal/review/internal/repository/dao/init.go b/internal/review/internal/repository/dao/init.go new file mode 100644 index 0000000..7e42929 --- /dev/null +++ b/internal/review/internal/repository/dao/init.go @@ -0,0 +1,10 @@ +package dao + +import "github.com/ego-component/egorm" + +func InitTables(db *egorm.Component) error { + return db.AutoMigrate( + &Review{}, + &PublishReview{}, + ) +} diff --git a/internal/review/internal/repository/dao/review.go b/internal/review/internal/repository/dao/review.go new file mode 100644 index 0000000..3c2a7cd --- /dev/null +++ b/internal/review/internal/repository/dao/review.go @@ -0,0 +1,144 @@ +package dao + +import ( + "context" + "time" + + "github.com/ecodeclub/webook/internal/review/internal/domain" + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "github.com/ego-component/egorm" +) + +type ReviewDAO interface { + // Create 创建一条面试评测记录 + Save(ctx context.Context, review Review) (int64, error) + + // Get 根据ID获取面试评测记录 + Get(ctx context.Context, id int64) (Review, error) + + // List 获取面试评测记录列表,支持分页 + List(ctx context.Context, offset, limit int) ([]Review, error) + + // Count 获取面试评测记录总数 + Count(ctx context.Context) (int64, error) + + // Sync 同步到线上库 + Sync(ctx context.Context, c Review) (int64, error) + PublishReviewList(ctx context.Context, offset, limit int) ([]PublishReview, error) + GetPublishReview(ctx context.Context, reviewId int64) (PublishReview, error) +} + +type reviewDao struct { + db *egorm.Component +} + +func NewReviewDAO(db *egorm.Component) ReviewDAO { + return &reviewDao{ + db: db, + } +} + +func (r *reviewDao) Save(ctx context.Context, review Review) (int64, error) { + now := time.Now().UnixMilli() + review.Utime = now + review.Ctime = now + return r.save(r.db.WithContext(ctx), review) +} + +func (r *reviewDao) Get(ctx context.Context, id int64) (Review, error) { + var review Review + err := r.db.WithContext(ctx).Where("id = ?", id).First(&review).Error + if err != nil { + return Review{}, err + } + return review, nil +} + +func (r *reviewDao) List(ctx context.Context, offset, limit int) ([]Review, error) { + var reviews []Review + err := r.db.WithContext(ctx). + Order("id desc"). + Offset(offset). + Limit(limit). + Find(&reviews).Error + if err != nil { + return nil, err + } + return reviews, nil +} + +func (r *reviewDao) Count(ctx context.Context) (int64, error) { + var count int64 + err := r.db.WithContext(ctx).Model(&Review{}).Count(&count).Error + return count, err +} + +func (r *reviewDao) save(db *gorm.DB, review Review) (int64, error) { + + err := db.Clauses(clause.OnConflict{ + DoUpdates: clause.AssignmentColumns([]string{ + "jd", + "jd_analysis", + "questions", + "question_analysis", + "resume", + "utime", + "status", + }), + }).Create(&review).Error + return review.ID, err +} + +func (r *reviewDao) Sync(ctx context.Context, c Review) (int64, error) { + var id = c.ID + now := time.Now().UnixMilli() + c.Ctime = now + c.Utime = now + err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var err error + id, err = r.save(tx, c) + if err != nil { + return err + } + pubReview := PublishReview(c) + return tx.Clauses(clause.OnConflict{ + DoUpdates: clause.AssignmentColumns([]string{ + "jd", + "jd_analysis", + "questions", + "question_analysis", + "resume", + "utime", + "status", + }), + }).Create(&pubReview).Error + }) + return id, err +} + +func (r *reviewDao) PublishReviewList(ctx context.Context, offset, limit int) ([]PublishReview, error) { + var publishReviews []PublishReview + err := r.db.WithContext(ctx). + Offset(offset). + Limit(limit). + Order("id DESC"). // 按ID降序排序,最新的记录在前面 + Where("status = ?", domain.PublishedStatus). + Find(&publishReviews).Error + if err != nil { + return nil, err + } + return publishReviews, nil +} + +func (r *reviewDao) GetPublishReview(ctx context.Context, reviewId int64) (PublishReview, error) { + var publishReview PublishReview + err := r.db.WithContext(ctx). + Where("id = ?", reviewId). + First(&publishReview).Error + if err != nil { + return PublishReview{}, err + } + return publishReview, nil +} diff --git a/internal/review/internal/repository/dao/type.go b/internal/review/internal/repository/dao/type.go new file mode 100644 index 0000000..4927ccf --- /dev/null +++ b/internal/review/internal/repository/dao/type.go @@ -0,0 +1,16 @@ +package dao + +type Review struct { + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + Uid int64 `gorm:"column:uid"` + JD string `gorm:"column:jd;type:text"` + JDAnalysis string `gorm:"column:jd_analysis;type:text"` + Questions string `gorm:"column:questions;type:text"` + QuestionAnalysis string `gorm:"column:question_analysis;type:text"` + Resume string `gorm:"column:resume;type:text"` + Status uint8 `gorm:"type:tinyint(3);comment:0-未知 1-未发表 2-已发表"` + Ctime int64 `gorm:"column:ctime"` + Utime int64 `gorm:"column:utime"` +} + +type PublishReview Review diff --git a/internal/review/internal/repository/review.go b/internal/review/internal/repository/review.go new file mode 100644 index 0000000..b1d99eb --- /dev/null +++ b/internal/review/internal/repository/review.go @@ -0,0 +1,108 @@ +package repository + +import ( + "context" + + "github.com/ecodeclub/ekit/slice" + "github.com/ecodeclub/webook/internal/review/internal/domain" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" +) + +type ReviewRepo interface { + Save(ctx context.Context, re domain.Review) (int64, error) + List(ctx context.Context, offset, limit int) ([]domain.Review, error) + Count(ctx context.Context) (int64, error) + Info(ctx context.Context, id int64) (domain.Review, error) + + Publish(ctx context.Context, re domain.Review) (int64, error) + PubList(ctx context.Context, offset, limit int) ([]domain.Review, error) + PubInfo(ctx context.Context, id int64) (domain.Review, error) +} +type reviewRepo struct { + reviewDao dao.ReviewDAO +} + +func NewReviewRepo(reviewDao dao.ReviewDAO) ReviewRepo { + return &reviewRepo{ + reviewDao: reviewDao, + } +} + +func (r *reviewRepo) Save(ctx context.Context, re domain.Review) (int64, error) { + daoReview := toDaoReview(re) + return r.reviewDao.Save(ctx, daoReview) +} + +func (r *reviewRepo) List(ctx context.Context, offset, limit int) ([]domain.Review, error) { + reviews, err := r.reviewDao.List(ctx, offset, limit) + if err != nil { + return nil, err + } + list := slice.Map(reviews, func(idx int, src dao.Review) domain.Review { + return toDomainReview(src) + }) + return list, nil +} + +func (r *reviewRepo) Count(ctx context.Context) (int64, error) { + return r.reviewDao.Count(ctx) +} + +func (r *reviewRepo) Info(ctx context.Context, id int64) (domain.Review, error) { + review, err := r.reviewDao.Get(ctx, id) + if err != nil { + return domain.Review{}, err + } + return toDomainReview(review), nil +} + +func (r *reviewRepo) Publish(ctx context.Context, re domain.Review) (int64, error) { + return r.reviewDao.Sync(ctx, toDaoReview(re)) +} + +func (r *reviewRepo) PubList(ctx context.Context, offset, limit int) ([]domain.Review, error) { + pubReviews, err := r.reviewDao.PublishReviewList(ctx, offset, limit) + if err != nil { + return nil, err + } + list := slice.Map(pubReviews, func(idx int, src dao.PublishReview) domain.Review { + return toDomainReview(dao.Review(src)) + }) + return list, nil +} + +func (r *reviewRepo) PubInfo(ctx context.Context, id int64) (domain.Review, error) { + pubReview, err := r.reviewDao.GetPublishReview(ctx, id) + if err != nil { + return domain.Review{}, err + } + return toDomainReview(dao.Review(pubReview)), nil +} + +// 将 domain.Review 转换为 dao.Review +func toDaoReview(review domain.Review) dao.Review { + return dao.Review{ + ID: review.ID, + Uid: review.Uid, + JD: review.JD, + JDAnalysis: review.JDAnalysis, + Questions: review.Questions, + QuestionAnalysis: review.QuestionAnalysis, + Status: review.Status.ToUint8(), + Resume: review.Resume, + } +} + +// 将 dao.Review 转换为 domain.Review +func toDomainReview(review dao.Review) domain.Review { + return domain.Review{ + ID: review.ID, + JD: review.JD, + JDAnalysis: review.JDAnalysis, + Questions: review.Questions, + QuestionAnalysis: review.QuestionAnalysis, + Resume: review.Resume, + Status: domain.ReviewStatus(review.Status), + Utime: review.Utime, + } +} diff --git a/internal/review/internal/service/review.go b/internal/review/internal/service/review.go new file mode 100644 index 0000000..be4165c --- /dev/null +++ b/internal/review/internal/service/review.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + + "github.com/ecodeclub/webook/internal/review/internal/domain" + "github.com/ecodeclub/webook/internal/review/internal/repository" + "golang.org/x/sync/errgroup" +) + +type ReviewSvc interface { + Save(ctx context.Context, re domain.Review) (int64, error) + List(ctx context.Context, offset, limit int) (int64, []domain.Review, error) + + Info(ctx context.Context, id int64) (domain.Review, error) + + Publish(ctx context.Context, re domain.Review) (int64, error) + PubList(ctx context.Context, offset, limit int) ([]domain.Review, error) + PubInfo(ctx context.Context, id int64) (domain.Review, error) +} + +func NewReviewSvc(repo repository.ReviewRepo) ReviewSvc { + return &reviewSvc{ + repo: repo, + } +} + +type reviewSvc struct { + repo repository.ReviewRepo +} + +func (r *reviewSvc) Save(ctx context.Context, re domain.Review) (int64, error) { + re.Status = domain.UnPublishedStatus + return r.repo.Save(ctx, re) +} + +func (r *reviewSvc) List(ctx context.Context, offset, limit int) (int64, []domain.Review, error) { + var eg errgroup.Group + var count int64 + var reviews []domain.Review + eg.Go(func() error { + var eerr error + reviews, eerr = r.repo.List(ctx, offset, limit) + return eerr + }) + eg.Go(func() error { + var eerr error + count, eerr = r.repo.Count(ctx) + return eerr + }) + err := eg.Wait() + return count, reviews, err + +} + +func (r *reviewSvc) Info(ctx context.Context, id int64) (domain.Review, error) { + return r.repo.Info(ctx, id) +} + +func (r *reviewSvc) Publish(ctx context.Context, re domain.Review) (int64, error) { + re.Status = domain.PublishedStatus + return r.repo.Publish(ctx, re) +} + +func (r *reviewSvc) PubList(ctx context.Context, offset, limit int) ([]domain.Review, error) { + return r.repo.PubList(ctx, offset, limit) +} + +func (r *reviewSvc) PubInfo(ctx context.Context, id int64) (domain.Review, error) { + return r.repo.PubInfo(ctx, id) +} diff --git a/internal/review/internal/web/admin_handler.go b/internal/review/internal/web/admin_handler.go new file mode 100644 index 0000000..9ffcdda --- /dev/null +++ b/internal/review/internal/web/admin_handler.go @@ -0,0 +1,78 @@ +package web + +import ( + "github.com/ecodeclub/ekit/slice" + "github.com/ecodeclub/ginx" + "github.com/ecodeclub/ginx/session" + "github.com/ecodeclub/webook/internal/review/internal/domain" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/gin-gonic/gin" +) + +type AdminHandler struct { + svc service.ReviewSvc +} + +func (h *AdminHandler) PrivateRoutes(server *gin.Engine) { + server.POST("/review/save", ginx.BS[ReviewSaveReq](h.Save)) + server.POST("/review/list", ginx.B[Page](h.List)) + server.POST("/review/detail", ginx.B[DetailReq](h.Detail)) + server.POST("/review/publish", ginx.BS[ReviewSaveReq](h.Publish)) +} + +func NewAdminHandler(svc service.ReviewSvc) *AdminHandler { + return &AdminHandler{ + svc: svc, + } +} + +func (h *AdminHandler) Save(ctx *ginx.Context, req ReviewSaveReq, sess session.Session) (ginx.Result, error) { + uid := sess.Claims().Uid + review := req.Review.toDomain() + review.Uid = uid + id, err := h.svc.Save(ctx, review) + if err != nil { + return systemErrorResult, err + } + return ginx.Result{ + Data: id, + }, nil +} + +func (h *AdminHandler) List(ctx *ginx.Context, req Page) (ginx.Result, error) { + count, reviewList, err := h.svc.List(ctx, req.Offset, req.Limit) + if err != nil { + return systemErrorResult, err + } + return ginx.Result{ + Data: ReviewListResp{ + Total: count, + List: slice.Map(reviewList, func(idx int, src domain.Review) Review { + return newReview(src) + }), + }, + }, nil +} + +func (h *AdminHandler) Detail(ctx *ginx.Context, req DetailReq) (ginx.Result, error) { + re, err := h.svc.Info(ctx, req.ID) + if err != nil { + return systemErrorResult, err + } + return ginx.Result{ + Data: newReview(re), + }, nil +} + +func (h *AdminHandler) Publish(ctx *ginx.Context, req ReviewSaveReq, sess session.Session) (ginx.Result, error) { + uid := sess.Claims().Uid + review := req.Review.toDomain() + review.Uid = uid + id, err := h.svc.Publish(ctx, review) + if err != nil { + return systemErrorResult, err + } + return ginx.Result{ + Data: id, + }, nil +} diff --git a/internal/review/internal/web/handler.go b/internal/review/internal/web/handler.go new file mode 100644 index 0000000..513127e --- /dev/null +++ b/internal/review/internal/web/handler.go @@ -0,0 +1,55 @@ +package web + +import ( + "github.com/ecodeclub/ekit/slice" + "github.com/ecodeclub/ginx" + "github.com/ecodeclub/webook/internal/review/internal/domain" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/gin-gonic/gin" +) + +type Handler struct { + svc service.ReviewSvc +} + +func NewHandler(svc service.ReviewSvc) *Handler { + return &Handler{ + svc: svc, + } +} + +func (h *Handler) PublicRoutes(server *gin.Engine) { + server.POST("/review/pub/list", ginx.B[Page](h.PubList)) +} +func (h *Handler) MemberRoutes(server *gin.Engine) { + server.POST("/review/pub/detail", ginx.B[DetailReq](h.PubDetail)) +} +func (h *Handler) PubList(ctx *ginx.Context, req Page) (ginx.Result, error) { + // 调用 service 层获取数据 + reviews, err := h.svc.PubList(ctx, req.Offset, req.Limit) + if err != nil { + return systemErrorResult, err + } + list := slice.Map(reviews, func(idx int, src domain.Review) Review { + return newReview(src) + }) + // 返回结果 + return ginx.Result{ + Data: ReviewListResp{List: list}, + }, nil +} + +// PubDetail 获取已发布的面试评测记录详情 +func (h *Handler) PubDetail(ctx *ginx.Context, req DetailReq) (ginx.Result, error) { + + // 调用 service 层获取数据 + review, err := h.svc.PubInfo(ctx, req.ID) + if err != nil { + return systemErrorResult, err + } + + // 转换为展示层对象并返回 + return ginx.Result{ + Data: newReview(review), + }, nil +} diff --git a/internal/review/internal/web/result.go b/internal/review/internal/web/result.go new file mode 100644 index 0000000..f18577b --- /dev/null +++ b/internal/review/internal/web/result.go @@ -0,0 +1,13 @@ +package web + +import ( + "github.com/ecodeclub/ginx" + "github.com/ecodeclub/webook/internal/review/internal/errs" +) + +var ( + systemErrorResult = ginx.Result{ + Code: errs.SystemError.Code, + Msg: errs.SystemError.Msg, + } +) diff --git a/internal/review/internal/web/vo.go b/internal/review/internal/web/vo.go new file mode 100644 index 0000000..5af619f --- /dev/null +++ b/internal/review/internal/web/vo.go @@ -0,0 +1,57 @@ +package web + +import "github.com/ecodeclub/webook/internal/review/internal/domain" + +type ReviewSaveReq struct { + Review Review `json:"review"` +} + +type Review struct { + ID int64 `json:"id,omitempty"` + JD string `json:"jd,omitempty"` + JDAnalysis string `json:"jd_analysis,omitempty"` + Questions string `json:"questions,omitempty"` + QuestionAnalysis string `json:"question_analysis,omitempty"` + Resume string `json:"resume,omitempty"` + Status uint8 `json:"status,omitempty"` + Utime int64 `json:"utime,omitempty"` +} + +type ReviewListResp struct { + Total int64 `json:"total"` + List []Review `json:"list"` +} + +func newReview(re domain.Review) Review { + return Review{ + ID: re.ID, + JD: re.JD, + JDAnalysis: re.JDAnalysis, + Questions: re.Questions, + QuestionAnalysis: re.QuestionAnalysis, + Resume: re.Resume, + Status: re.Status.ToUint8(), + Utime: re.Utime, + } +} + +func (r Review) toDomain() domain.Review { + return domain.Review{ + ID: r.ID, + JD: r.JD, + JDAnalysis: r.JDAnalysis, + Questions: r.Questions, + QuestionAnalysis: r.QuestionAnalysis, + Resume: r.Resume, + Status: domain.ReviewStatus(r.Status), + Utime: r.Utime, + } +} + +type DetailReq struct { + ID int64 `json:"id,omitempty"` +} +type Page struct { + Offset int `json:"offset,omitempty"` + Limit int `json:"limit,omitempty"` +} diff --git a/internal/review/module.go b/internal/review/module.go new file mode 100644 index 0000000..74791b5 --- /dev/null +++ b/internal/review/module.go @@ -0,0 +1,10 @@ +package review + +import "github.com/ecodeclub/webook/internal/review/internal/web" + +type Module struct { + Hdl *Hdl + AdminHdl *AdminHdl +} +type AdminHdl = web.AdminHandler +type Hdl = web.Handler diff --git a/internal/review/wire.go b/internal/review/wire.go new file mode 100644 index 0000000..60d6130 --- /dev/null +++ b/internal/review/wire.go @@ -0,0 +1,32 @@ +//go:build wireinject + +package review + +import ( + "github.com/ecodeclub/webook/internal/review/internal/repository" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/ecodeclub/webook/internal/review/internal/web" + "github.com/ego-component/egorm" + "github.com/google/wire" +) + +func InitModule(db *egorm.Component) *Module { + wire.Build( + initReviewDao, + repository.NewReviewRepo, + service.NewReviewSvc, + web.NewHandler, + web.NewAdminHandler, + wire.Struct(new(Module), "*"), + ) + return new(Module) +} + +func initReviewDao(db *egorm.Component) dao.ReviewDAO { + err := dao.InitTables(db) + if err != nil { + panic(err) + } + return dao.NewReviewDAO(db) +} diff --git a/internal/review/wire_gen.go b/internal/review/wire_gen.go new file mode 100644 index 0000000..5e01d51 --- /dev/null +++ b/internal/review/wire_gen.go @@ -0,0 +1,41 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package review + +import ( + "github.com/ecodeclub/webook/internal/review/internal/repository" + "github.com/ecodeclub/webook/internal/review/internal/repository/dao" + "github.com/ecodeclub/webook/internal/review/internal/service" + "github.com/ecodeclub/webook/internal/review/internal/web" + "github.com/ego-component/egorm" + "gorm.io/gorm" +) + +// Injectors from wire.go: + +func InitModule(db *gorm.DB) *Module { + reviewDAO := initReviewDao(db) + reviewRepo := repository.NewReviewRepo(reviewDAO) + reviewSvc := service.NewReviewSvc(reviewRepo) + handler := web.NewHandler(reviewSvc) + adminHandler := web.NewAdminHandler(reviewSvc) + module := &Module{ + Hdl: handler, + AdminHdl: adminHandler, + } + return module +} + +// wire.go: + +func initReviewDao(db *egorm.Component) dao.ReviewDAO { + err := dao.InitTables(db) + if err != nil { + panic(err) + } + return dao.NewReviewDAO(db) +} diff --git a/ioc/admin.go b/ioc/admin.go index aba8d06..6564a8a 100644 --- a/ioc/admin.go +++ b/ioc/admin.go @@ -18,6 +18,8 @@ import ( "net/http" "strings" + "github.com/ecodeclub/webook/internal/review" + "github.com/ecodeclub/webook/internal/ai" "github.com/ecodeclub/webook/internal/cases" @@ -47,6 +49,7 @@ func InitAdminServer(prj *project.AdminHandler, caseSetHdl *cases.AdminCaseSetHandler, mark *marketing.AdminHandler, aiHdl *ai.AdminHandler, + reviewAdminHdl *review.AdminHdl, ) AdminServer { res := egin.Load("admin").Build() res.Use(cors.New(cors.Config{ @@ -78,6 +81,7 @@ func InitAdminServer(prj *project.AdminHandler, caseHdl.PrivateRoutes(res.Engine) caseSetHdl.PrivateRoutes(res.Engine) aiHdl.RegisterRoutes(res.Engine) + reviewAdminHdl.PrivateRoutes(res.Engine) return res } diff --git a/ioc/gin.go b/ioc/gin.go index 2b3a80c..1b97e3f 100644 --- a/ioc/gin.go +++ b/ioc/gin.go @@ -18,6 +18,8 @@ import ( "net/http" "strings" + "github.com/ecodeclub/webook/internal/review" + "github.com/ecodeclub/webook/internal/ai" "github.com/ecodeclub/webook/internal/resume" @@ -90,6 +92,7 @@ func initGinxServer(sp session.Provider, resumePrjHdl *resume.ProjectHandler, resumeAnaHdl *resume.AnalysisHandler, aiHdl *ai.LLMHandler, + reviewHdl *review.Hdl, ) *egin.Component { session.SetDefaultProvider(sp) res := egin.Load("web").Build() @@ -133,7 +136,7 @@ func initGinxServer(sp session.Provider, skillHdl.PublicRoutes(res.Engine) csHdl.PublicRoutes(res.Engine) prjHdl.PublicRoutes(res.Engine) - + reviewHdl.PublicRoutes(res.Engine) // 登录校验 res.Use(session.CheckLoginMiddleware()) user.PrivateRoutes(res.Engine) @@ -165,5 +168,6 @@ func initGinxServer(sp session.Provider, resumePrjHdl.MemberRoutes(res.Engine) resumeAnaHdl.MemberRoutes(res.Engine) aiHdl.MemberRoutes(res.Engine) + reviewHdl.MemberRoutes(res.Engine) return res } diff --git a/ioc/wire.go b/ioc/wire.go index 2bf3903..22394fb 100644 --- a/ioc/wire.go +++ b/ioc/wire.go @@ -36,6 +36,7 @@ import ( baguwen "github.com/ecodeclub/webook/internal/question" "github.com/ecodeclub/webook/internal/recon" "github.com/ecodeclub/webook/internal/resume" + "github.com/ecodeclub/webook/internal/review" "github.com/ecodeclub/webook/internal/roadmap" "github.com/ecodeclub/webook/internal/search" "github.com/ecodeclub/webook/internal/skill" @@ -93,6 +94,8 @@ func InitApp() (*App, error) { resume.InitModule, wire.FieldsOf(new(*resume.Module), "PrjHdl", "AnalysisHandler"), wire.FieldsOf(new(*ai.Module), "Hdl", "AdminHandler"), + review.InitModule, + wire.FieldsOf(new(*review.Module), "Hdl", "AdminHdl"), initLocalActiveLimiterBuilder, initCronJobs, diff --git a/ioc/wire_gen.go b/ioc/wire_gen.go index 9234ffe..d7d0877 100644 --- a/ioc/wire_gen.go +++ b/ioc/wire_gen.go @@ -1,6 +1,6 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run github.com/google/wire/cmd/wire +//go:generate go run -mod=mod github.com/google/wire/cmd/wire //go:build !wireinject // +build !wireinject @@ -26,6 +26,7 @@ import ( baguwen "github.com/ecodeclub/webook/internal/question" "github.com/ecodeclub/webook/internal/recon" "github.com/ecodeclub/webook/internal/resume" + "github.com/ecodeclub/webook/internal/review" "github.com/ecodeclub/webook/internal/roadmap" "github.com/ecodeclub/webook/internal/search" "github.com/ecodeclub/webook/internal/skill" @@ -135,7 +136,9 @@ func InitApp() (*App, error) { projectHandler := resumeModule.PrjHdl analysisHandler := resumeModule.AnalysisHandler handler17 := aiModule.Hdl - component := initGinxServer(provider, checkMembershipMiddlewareBuilder, localActiveLimit, checkPermissionMiddlewareBuilder, handler, examineHandler, questionSetHandler, webHandler, handler2, handler3, handler4, handler5, handler6, handler7, handler8, handler9, handler10, handler11, handler12, handler13, handler14, handler15, handler16, caseSetHandler, webExamineHandler, projectHandler, analysisHandler, handler17) + reviewModule := review.InitModule(db) + handler18 := reviewModule.Hdl + component := initGinxServer(provider, checkMembershipMiddlewareBuilder, localActiveLimit, checkPermissionMiddlewareBuilder, handler, examineHandler, questionSetHandler, webHandler, handler2, handler3, handler4, handler5, handler6, handler7, handler8, handler9, handler10, handler11, handler12, handler13, handler14, handler15, handler16, caseSetHandler, webExamineHandler, projectHandler, analysisHandler, handler17, handler18) adminHandler := projectModule.AdminHdl webAdminHandler := roadmapModule.AdminHdl adminHandler2 := baguwenModule.AdminHdl @@ -144,7 +147,8 @@ func InitApp() (*App, error) { adminCaseSetHandler := casesModule.AdminSetHandler adminHandler3 := marketingModule.AdminHdl adminHandler4 := aiModule.AdminHandler - adminServer := InitAdminServer(adminHandler, webAdminHandler, adminHandler2, adminQuestionSetHandler, adminCaseHandler, adminCaseSetHandler, adminHandler3, adminHandler4) + adminHandler5 := reviewModule.AdminHdl + adminServer := InitAdminServer(adminHandler, webAdminHandler, adminHandler2, adminQuestionSetHandler, adminCaseHandler, adminCaseSetHandler, adminHandler3, adminHandler4, adminHandler5) closeTimeoutOrdersJob := orderModule.CloseTimeoutOrdersJob closeTimeoutLockedCreditsJob := creditModule.CloseTimeoutLockedCreditsJob syncWechatOrderJob := paymentModule.SyncWechatOrderJob