diff --git a/frontend/.editorconfig b/.editorconfig
similarity index 94%
rename from frontend/.editorconfig
rename to .editorconfig
index fbace8d..0880aa5 100644
--- a/frontend/.editorconfig
+++ b/.editorconfig
@@ -5,6 +5,7 @@ root = true
charset = utf-8
indent_style = space
indent_size = 2
+tab_width = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
@@ -12,4 +13,3 @@ trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
-
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b1d7852
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,71 @@
+# Common
+.DS_Store
+.idea/
+*.iws
+*.iml
+*.ipr
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.log
+*.swp
+
+# Frontend workspace
+/frontend/**/node_modules/
+/frontend/**/.eslintcache
+/frontend/**/tsconfig.tsbuildinfo
+/frontend/**/*.local
+
+# Frontend app
+/frontend/app/dist/
+/frontend/app/deploy_versions/
+/frontend/app/.temp/
+/frontend/app/.rn_temp/
+/frontend/app/.swc
+
+# Frontend web
+/frontend/web/dist/
+/frontend/web/dist-ssr/
+/frontend/web/report.html
+/frontend/web/yarn.lock
+/frontend/web/npm-debug.log*
+/frontend/web/.pnpm-error.log*
+/frontend/web/.pnpm-debug.log
+/frontend/web/tests/**/coverage/
+/frontend/web/.vscode/launch.json
+
+# Backend build tools
+/backend/.gradle/
+/backend/build/
+!/backend/gradle/wrapper/gradle-wrapper.jar
+/backend/**/target/
+!/backend/.mvn/wrapper/maven-wrapper.jar
+
+# Backend IDE
+/backend/**/.apt_generated
+/backend/**/.classpath
+/backend/**/.factorypath
+/backend/**/.project
+/backend/**/.settings/
+/backend/**/.springBeans
+
+# Backend JRebel
+/backend/**/rebel.xml
+
+# Backend NetBeans
+/backend/nbproject/private/
+/backend/build/*
+/backend/nbbuild/
+/backend/dist/
+/backend/nbdist/
+/backend/.nb-gradle/
+
+# Backend generated files
+/backend/**/*.xml.versionsBackup
+!/backend/*/build/*.java
+!/backend/*/build/*.html
+!/backend/*/build/*.xml
+
+# Backend local configuration
+/backend/agileboot-admin/src/main/resources/application-prod.yml
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..e70e633
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,12 @@
+{
+ "recommendations": [
+ "EditorConfig.EditorConfig",
+ "esbenp.prettier-vscode",
+ "dbaeumer.vscode-eslint",
+ "stylelint.vscode-stylelint",
+ "Vue.volar",
+ "bradlc.vscode-tailwindcss",
+ "mikestead.dotenv",
+ "antfu.iconify"
+ ]
+}
diff --git a/frontend/web/.vscode/settings.json b/.vscode/settings.json
similarity index 53%
rename from frontend/web/.vscode/settings.json
rename to .vscode/settings.json
index 9b434de..2e05277 100644
--- a/frontend/web/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,32 +1,43 @@
{
- "editor.formatOnType": true,
- "editor.formatOnSave": true,
- "[vue]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
- },
+ "editor.detectIndentation": false,
"editor.tabSize": 2,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
"editor.formatOnPaste": true,
+ "editor.formatOnType": true,
"editor.guides.bracketPairs": "active",
- "files.autoSave": "afterDelay",
- "git.confirmSync": false,
- "workbench.startupEditor": "newUntitledFile",
+ "editor.snippetSuggestions": "top",
"editor.suggestSelection": "first",
"editor.acceptSuggestionOnCommitCharacter": false,
- "css.lint.propertyIgnoredDueToDisplay": "ignore",
"editor.quickSuggestions": {
"other": true,
"comments": true,
"strings": true
},
- "files.associations": {
- "editor.snippetSuggestions": "top"
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.fixAll.stylelint": "explicit"
+ },
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
+ "stylelint.validate": ["css", "scss", "vue"],
+ "[vue]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
- "editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "[scss]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
},
- "iconify.excludes": ["el"],
- "cSpell.words": ["iconify", "Qrcode"]
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "css.lint.propertyIgnoredDueToDisplay": "ignore",
+ "files.autoSave": "afterDelay",
+ "git.confirmSync": false,
+ "workbench.startupEditor": "newUntitledFile",
+ "iconify.excludes": ["el"]
}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..4f85cd9
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,15 @@
+# Agent Rules
+
+- 新增或修改代码前,必须阅读并遵循 `docs/clean-code-contract.md`。
+- 后端新增或修改业务功能前,必须阅读 `docs/backend-feature-development.md`。
+- 后端代码必须遵循 `Controller -> ApplicationService -> Model -> db Service/Mapper` 的组织方式。
+- 不要把业务规则写在 Controller。
+- 不要让 Controller 直接调用 Mapper。
+- 不要直接把 Entity 或 DO 返回给前端。
+- 复杂查询结果对象可以使用 `XxxDO`,放在 `db` 包下,并由 ApplicationService 转换为 DTO。
+- 字典类需求不要默认创建字典管理表;本项目现有字典数据使用 Enum 和缓存。
+- `frontend/web` 新增或修改业务功能前,必须阅读 `docs/web-feature-development.md`。
+- Web 前端接口必须通过 `@/utils/http` 封装调用,不要直接使用 Axios。
+- Web 列表页复杂状态和行为应放在 `utils/hook.tsx`,不要堆在 `index.vue`。
+- Web 页面私有组件放在当前模块的 `components/`,多模块复用组件提升到 `frontend/web/src/components`。
+- Web 字典展示优先使用 `useUserStoreHook().dictionaryList` 或 `dictionaryMap`,不要硬编码状态文本和值。
diff --git a/README.md b/README.md
index cae1036..6c9afb8 100644
--- a/README.md
+++ b/README.md
@@ -34,8 +34,31 @@ cd backend
docker compose up -d
```
-On a fresh Docker volume, Compose creates the MySQL database `agileboot_pure`.
-Import the SQL files under `backend/sql/` before starting the backend.
+The compose configuration mounts `backend/sql/agileboot.sql` into the MySQL
+container as `/docker-entrypoint-initdb.d/01-agileboot.sql`.
+The official MySQL image only runs files in `/docker-entrypoint-initdb.d` when
+the database directory is empty, during the first initialization of the
+`mysql_data` volume.
+
+If MySQL has already been started before, the `mysql_data` volume already
+contains data and `docker compose up -d` will not import the SQL again. To
+reinitialize the local database, remove the volumes first:
+
+```bash
+cd backend
+docker compose down -v
+docker compose up -d
+```
+
+Warning: `docker compose down -v` deletes the local MySQL and Redis volumes,
+including existing database data and Redis data.
+
+If you do not want to delete the volumes, import the SQL manually:
+
+```bash
+cd backend
+docker exec -i mysql-server mysql -uroot -proot123 agileboot_pure < sql/agileboot.sql
+```
### Install Frontend Dependencies
@@ -50,7 +73,7 @@ Start the backend:
```bash
cd backend
-./mvnw -pl agileboot-admin -am spring-boot:run
+./mvnw -pl agileboot-admin spring-boot:run
```
Start the web frontend:
diff --git a/backend/.github/ISSUE_TEMPLATE/bug_report.md b/backend/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index ab0e815..0000000
--- a/backend/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-name: Bug 报告
-about: 创建BUG报告以改进项目
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**BUG描述**
-关于BUG清晰简洁的描述。
-
-**复现步骤**
-详细的复现步骤。
-
-
-**正确的行为**
-你认为这个修复这个BUG后,正确的行为应该是什么。
-
-
-**详细截图**
-如果可以的话,请添加截图以帮助调查BUG.
-
-**桌面端:**
- - 操作系统: [例如. iOS]
- - 浏览器及版本 [例如. chrome 11]
- - 项目版本 [例如. 1.6.0]
-
-**手机端:**
- - 设备: [例如. iPhone6]
- - 操作系统: [例如. iOS8.1]
- - 浏览器及版本 [例如.safari 8]
- - 项目版本 [例如. 1.6.0]
-
-**Additional context**
-任何其他你认为有助于排查错误的信息,或者你的猜测。
diff --git a/backend/.github/ISSUE_TEMPLATE/feature_request.md b/backend/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 913a980..0000000
--- a/backend/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: 功能建议
-about: 关于该项目的建议
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**您的功能请求是否与问题相关? 请描述。**
-清楚简明地描述问题所在。
-
-**描述您想要的解决方案**
-对您所设想的问题的清晰简洁的描述。
-
-**描述您考虑过的替代方案**
-对您考虑过的任何替代解决方案或功能的清晰简洁的描述。
-
-**附加上下文**
-在此处添加有关功能请求的任何其他上下文或屏幕截图。
diff --git a/backend/.github/workflows/ci-cd.yml b/backend/.github/workflows/ci-cd.yml
deleted file mode 100644
index 4a7a5a7..0000000
--- a/backend/.github/workflows/ci-cd.yml
+++ /dev/null
@@ -1,116 +0,0 @@
-# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
-
-# 权限声明,确保 workflow 有权限写 checks 和 security-events
-permissions:
- contents: read
- checks: write
- security-events: write
-
-name: Java CI with Maven
-
-on:
- push:
- branches: [ "main" ]
- paths-ignore:
- - 'README.md'
- - 'LICENSE'
- - '.gitignore'
- - '.gitattributes'
- - 'picture'
- pull_request:
- branches: [ "main" ]
- workflow_dispatch:
-
-jobs:
- build:
- runs-on: ubuntu-latest
- timeout-minutes: 30
- strategy:
- matrix:
- java-version: ['8', '17', '21']
- fail-fast: false
-
- name: Build with Java ${{ matrix.java-version }}
-
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- - name: Set up JDK ${{ matrix.java-version }}
- uses: actions/setup-java@v3
- with:
- java-version: ${{ matrix.java-version }}
- distribution: 'temurin'
- cache: 'maven'
-
- # 优化Maven本地仓库缓存策略
- - name: Cache Maven packages
- uses: actions/cache@v3
- with:
- path: ~/.m2
- key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}-${{ matrix.java-version }}
- restore-keys: |
- ${{ runner.os }}-m2-
-
- # 编译和测试:去掉failOnWarning,避免因为警告导致失败
- - name: Build and Test with Maven
- run: |
- mvn -B verify --file pom.xml -Dmaven.test.failure.ignore=false -Dgpg.skip -Dmaven.javadoc.skip=false
- env:
- MAVEN_OPTS: -Xmx4g -XX:MaxMetaspaceSize=1g
- MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version"
-
- - name: Publish Test Report
- uses: mikepenz/action-junit-report@v4
- if: success() || failure()
- with:
- report_paths: '**/target/surefire-reports/TEST-*.xml'
- detailed_summary: true
- include_passed: true
- fail_on_failure: true
-
- - name: Run SonarQube Analysis
- if: matrix.java-version == '17' && github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
- continue-on-error: true
- run: |
- if [[ ! -z "${{ secrets.SONAR_TOKEN }}" ]]; then
- mvn sonar:sonar \
- -Dsonar.projectKey=agileboot \
- -Dsonar.organization=${{ secrets.SONAR_ORGANIZATION || 'default' }} \
- -Dsonar.host.url=${{ secrets.SONAR_HOST_URL || 'https://sonarcloud.io' }} \
- -Dsonar.login=${{ secrets.SONAR_TOKEN }} \
- -Dsonar.java.source=${{ matrix.java-version }}
- else
- echo "Skipping SonarQube analysis - SONAR_TOKEN not configured"
- fi
-
- # 上传构建产物,if-no-files-found 改为 warn
- - name: Upload Build Artifacts
- uses: actions/upload-artifact@v4
- with:
- name: agileboot-artifacts-java-${{ matrix.java-version }}
- path: |
- **/target/*.jar
- !**/target/original-*.jar
- retention-days: 5
- if-no-files-found: warn
-
- # # 只在 Java 17 版本上更新依赖图(权限和token已修复)
- # - name: Update dependency graph
- # uses: advanced-security/maven-dependency-submission-action@v4
- # if: matrix.java-version == '17' && success()
- # with:
- # token: ${{ secrets.GITHUB_TOKEN }}
-
- # # 发送构建状态通知
- # - name: Notify Build Status
- # if: always()
- # uses: rtCamp/action-slack-notify@v2.2.1
- # env:
- # SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK || '' }}
- # SLACK_CHANNEL: build-notifications
- # SLACK_COLOR: ${{ job.status }}
- # SLACK_TITLE: Build Status for Java ${{ matrix.java-version }}
- # SLACK_MESSAGE: 'Build ${{ job.status }} on Java ${{ matrix.java-version }}'
\ No newline at end of file
diff --git a/backend/.gitignore b/backend/.gitignore
deleted file mode 100644
index f6e0209..0000000
--- a/backend/.gitignore
+++ /dev/null
@@ -1,50 +0,0 @@
-######################################################################
-# Build Tools
-
-.gradle
-/build/
-!gradle/wrapper/gradle-wrapper.jar
-
-target/
-!.mvn/wrapper/maven-wrapper.jar
-
-######################################################################
-# IDE
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### JRebel ###
-rebel.xml
-
-### NetBeans ###
-nbproject/private/
-build/*
-nbbuild/
-dist/
-nbdist/
-.nb-gradle/
-
-######################################################################
-# Others
-*.log
-*.xml.versionsBackup
-*.swp
-
-!*/build/*.java
-!*/build/*.html
-!*/build/*.xml
-
-/agileboot-admin/src/main/resources/application-prod.yml
-
diff --git a/backend/GoogleStyle.xml b/backend/GoogleStyle.xml
index c2e1d68..1214d64 100644
--- a/backend/GoogleStyle.xml
+++ b/backend/GoogleStyle.xml
@@ -148,7 +148,9 @@
+
+
@@ -564,4 +566,4 @@
-
\ No newline at end of file
+
diff --git a/backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/collaboration/CollaborationRecordController.java b/backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/collaboration/CollaborationRecordController.java
new file mode 100644
index 0000000..44127c0
--- /dev/null
+++ b/backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/collaboration/CollaborationRecordController.java
@@ -0,0 +1,104 @@
+package com.agileboot.admin.controller.collaboration;
+
+import com.agileboot.admin.customize.aop.accessLog.AccessLog;
+import com.agileboot.common.core.base.BaseController;
+import com.agileboot.common.core.dto.ResponseDTO;
+import com.agileboot.common.core.page.PageDTO;
+import com.agileboot.common.enums.common.BusinessTypeEnum;
+import com.agileboot.domain.collaboration.record.CollaborationRecordApplicationService;
+import com.agileboot.domain.collaboration.record.command.AddCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.command.UpdateCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.dto.CollaborationMonthlyStatisticsDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationOptionDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationRecordDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationRecordDetailDTO;
+import com.agileboot.domain.collaboration.record.query.CollaborationRecordQuery;
+import com.agileboot.domain.common.command.BulkOperationCommand;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Positive;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author codex
+ */
+@Tag(name = "合作记录API", description = "合作记录相关的增删查改和统计")
+@RestController
+@RequestMapping("/collaboration/record")
+@Validated
+@RequiredArgsConstructor
+public class CollaborationRecordController extends BaseController {
+
+ private final CollaborationRecordApplicationService recordApplicationService;
+
+ @Operation(summary = "合作记录列表")
+ @PreAuthorize("@permission.has('collaboration:record:list')")
+ @GetMapping("/list")
+ public ResponseDTO> list(CollaborationRecordQuery query) {
+ return ResponseDTO.ok(recordApplicationService.getRecordList(query));
+ }
+
+ @Operation(summary = "合作记录详情")
+ @PreAuthorize("@permission.has('collaboration:record:query')")
+ @GetMapping("/{recordId}")
+ public ResponseDTO getInfo(@PathVariable @Positive Long recordId) {
+ return ResponseDTO.ok(recordApplicationService.getRecordInfo(recordId));
+ }
+
+ @Operation(summary = "合作记录选项")
+ @PreAuthorize("@permission.has('collaboration:record:list')")
+ @GetMapping("/options")
+ public ResponseDTO> options() {
+ return ResponseDTO.ok(recordApplicationService.getOptions());
+ }
+
+ @Operation(summary = "合作记录月度统计")
+ @PreAuthorize("@permission.has('collaboration:record:statistics')")
+ @GetMapping("/monthly-statistics")
+ public ResponseDTO> monthlyStatistics(@RequestParam Integer year) {
+ return ResponseDTO.ok(recordApplicationService.getMonthlyStatistics(year));
+ }
+
+ @Operation(summary = "新增合作记录")
+ @PreAuthorize("@permission.has('collaboration:record:add')")
+ @AccessLog(title = "合作记录", businessType = BusinessTypeEnum.ADD)
+ @PostMapping
+ public ResponseDTO add(@Valid @RequestBody AddCollaborationRecordCommand command) {
+ recordApplicationService.addRecord(command);
+ return ResponseDTO.ok();
+ }
+
+ @Operation(summary = "修改合作记录")
+ @PreAuthorize("@permission.has('collaboration:record:edit')")
+ @AccessLog(title = "合作记录", businessType = BusinessTypeEnum.MODIFY)
+ @PutMapping
+ public ResponseDTO edit(@Valid @RequestBody UpdateCollaborationRecordCommand command) {
+ recordApplicationService.updateRecord(command);
+ return ResponseDTO.ok();
+ }
+
+ @Operation(summary = "删除合作记录")
+ @PreAuthorize("@permission.has('collaboration:record:remove')")
+ @AccessLog(title = "合作记录", businessType = BusinessTypeEnum.DELETE)
+ @DeleteMapping
+ public ResponseDTO remove(@RequestParam @NotNull @NotEmpty List ids) {
+ recordApplicationService.deleteRecord(new BulkOperationCommand<>(ids));
+ return ResponseDTO.ok();
+ }
+
+}
diff --git a/backend/agileboot-admin/src/main/resources/application-dev.yml b/backend/agileboot-admin/src/main/resources/application-dev.yml
index 01956ea..eab641f 100644
--- a/backend/agileboot-admin/src/main/resources/application-dev.yml
+++ b/backend/agileboot-admin/src/main/resources/application-dev.yml
@@ -50,7 +50,7 @@ spring:
datasource:
# 主库数据源
master:
- url: jdbc:mysql://localhost:3306/agileboot_pure?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ url: jdbc:mysql://localhost:33061/todo_agileboot_pure?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root123
# 从库数据源
@@ -64,7 +64,7 @@ spring:
# 地址
host: localhost
# 端口,默认为6379
- port: 6379
+ port: 63791
# 数据库索引
database: 0
# 密码
@@ -84,7 +84,7 @@ spring:
logging:
file:
- path: D:/logs/agileboot-dev
+ path: /home/agileboot/logs/agileboot-dev
springdoc:
@@ -98,8 +98,8 @@ springdoc:
# 项目相关配置
agileboot:
- # 文件基路径 示例( Windows配置D:\agileboot,Linux配置 /home/agileboot)
- file-base-dir: D:\agileboot
+ # 文件基路径 示例(Linux配置 /home/agileboot)
+ file-base-dir: /home/agileboot
# 前端url请求转发前缀
api-prefix: /dev-api
demo-enabled: false
diff --git a/backend/agileboot-admin/src/test/java/com/agileboot/admin/config/AgileBootConfigTest.java b/backend/agileboot-admin/src/test/java/com/agileboot/admin/config/AgileBootConfigTest.java
index f739664..7a730dc 100644
--- a/backend/agileboot-admin/src/test/java/com/agileboot/admin/config/AgileBootConfigTest.java
+++ b/backend/agileboot-admin/src/test/java/com/agileboot/admin/config/AgileBootConfigTest.java
@@ -6,14 +6,11 @@ import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.Constants.UploadSubDir;
import java.io.File;
import javax.annotation.Resource;
-import org.junit.Test;
import org.junit.jupiter.api.Assertions;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest(classes = AgileBootAdminApplication.class)
-@RunWith(SpringRunner.class)
public class AgileBootConfigTest {
@Resource
@@ -21,7 +18,7 @@ public class AgileBootConfigTest {
@Test
public void testConfig() {
- String fileBaseDir = "D:\\agileboot\\profile";
+ String fileBaseDir = "/home/agileboot/profile";
Assertions.assertEquals("AgileBoot", config.getName());
Assertions.assertEquals("1.8.0", config.getVersion());
@@ -31,13 +28,13 @@ public class AgileBootConfigTest {
Assertions.assertFalse(AgileBootConfig.isAddressEnabled());
Assertions.assertEquals("math", AgileBootConfig.getCaptchaType());
Assertions.assertEquals("math", AgileBootConfig.getCaptchaType());
- Assertions.assertEquals(fileBaseDir + "\\import",
+ Assertions.assertEquals(fileBaseDir + "/import",
AgileBootConfig.getFileBaseDir() + File.separator + UploadSubDir.IMPORT_PATH);
- Assertions.assertEquals(fileBaseDir + "\\avatar",
+ Assertions.assertEquals(fileBaseDir + "/avatar",
AgileBootConfig.getFileBaseDir() + File.separator + UploadSubDir.AVATAR_PATH);
- Assertions.assertEquals(fileBaseDir + "\\download",
+ Assertions.assertEquals(fileBaseDir + "/download",
AgileBootConfig.getFileBaseDir() + File.separator + UploadSubDir.DOWNLOAD_PATH);
- Assertions.assertEquals(fileBaseDir + "\\upload",
+ Assertions.assertEquals(fileBaseDir + "/upload",
AgileBootConfig.getFileBaseDir() + File.separator + UploadSubDir.UPLOAD_PATH);
}
diff --git a/backend/agileboot-common/src/test/java/com/agileboot/common/utils/file/FileUploadUtilsTest.java b/backend/agileboot-common/src/test/java/com/agileboot/common/utils/file/FileUploadUtilsTest.java
index eddbc1a..7e92f86 100644
--- a/backend/agileboot-common/src/test/java/com/agileboot/common/utils/file/FileUploadUtilsTest.java
+++ b/backend/agileboot-common/src/test/java/com/agileboot/common/utils/file/FileUploadUtilsTest.java
@@ -110,10 +110,10 @@ class FileUploadUtilsTest {
@Test
void getFileAbsolutePath() {
AgileBootConfig agileBootConfig = new AgileBootConfig();
- agileBootConfig.setFileBaseDir("D:\\agileboot");
+ agileBootConfig.setFileBaseDir("/home/agileboot");
String fileAbsolutePath = FileUploadUtils.getFileAbsolutePath(UploadSubDir.AVATAR_PATH, "test.jpg");
- Assertions.assertEquals("D:\\agileboot\\profile\\avatar\\test.jpg", fileAbsolutePath);
+ Assertions.assertEquals("/home/agileboot/profile/avatar/test.jpg", fileAbsolutePath);
}
}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/CollaborationRecordApplicationService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/CollaborationRecordApplicationService.java
new file mode 100644
index 0000000..099b2d9
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/CollaborationRecordApplicationService.java
@@ -0,0 +1,402 @@
+package com.agileboot.domain.collaboration.record;
+
+import com.agileboot.common.core.page.PageDTO;
+import com.agileboot.domain.collaboration.record.command.AddCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.command.CollaborationExpenditureCommand;
+import com.agileboot.domain.collaboration.record.command.CollaborationFileCommand;
+import com.agileboot.domain.collaboration.record.command.CollaborationSettlementCommand;
+import com.agileboot.domain.collaboration.record.command.CollaborationTaskCommand;
+import com.agileboot.domain.collaboration.record.command.UpdateCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.db.CollaborationExpenditureEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationExpenditureService;
+import com.agileboot.domain.collaboration.record.db.CollaborationFileEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationFileService;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordService;
+import com.agileboot.domain.collaboration.record.db.CollaborationSettlementEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationSettlementService;
+import com.agileboot.domain.collaboration.record.db.CollaborationTaskEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationTaskService;
+import com.agileboot.domain.collaboration.record.dto.CollaborationExpenditureDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationFileDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationMonthlyStatisticsDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationOptionDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationRecordDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationRecordDetailDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationSettlementDTO;
+import com.agileboot.domain.collaboration.record.dto.CollaborationTaskDTO;
+import com.agileboot.domain.collaboration.record.dto.SettlementStatusDTO;
+import com.agileboot.domain.collaboration.record.enumtype.CollaborationOptionEnum;
+import com.agileboot.domain.collaboration.record.enumtype.SettlementStatusEnum;
+import com.agileboot.domain.collaboration.record.model.CollaborationRecordModel;
+import com.agileboot.domain.collaboration.record.model.CollaborationRecordModelFactory;
+import com.agileboot.domain.collaboration.record.query.CollaborationRecordQuery;
+import com.agileboot.domain.common.command.BulkOperationCommand;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author codex
+ */
+@Service
+@RequiredArgsConstructor
+public class CollaborationRecordApplicationService {
+
+ private static final String PURCHASE_FEE = "拍单费用";
+ private static final String DELIVERY_FEE = "快递费用";
+ private static final String REMUNERATION_FEE = "稿费";
+
+ private final CollaborationRecordModelFactory recordModelFactory;
+ private final CollaborationRecordService recordService;
+ private final CollaborationTaskService taskService;
+ private final CollaborationExpenditureService expenditureService;
+ private final CollaborationSettlementService settlementService;
+ private final CollaborationFileService fileService;
+
+ public PageDTO getRecordList(CollaborationRecordQuery query) {
+ Page page = recordService.page(query.toPage(), query.toQueryWrapper());
+ List records = page.getRecords().stream()
+ .map(this::buildRecordDTO)
+ .collect(Collectors.toList());
+ return new PageDTO<>(records, page.getTotal());
+ }
+
+ public CollaborationRecordDetailDTO getRecordInfo(Long recordId) {
+ CollaborationRecordModel model = recordModelFactory.loadById(recordId);
+ CollaborationRecordDetailDTO dto = new CollaborationRecordDetailDTO(model);
+ fillDetailChildren(dto, recordId);
+ fillRecordStats(dto, recordId);
+ return dto;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void addRecord(AddCollaborationRecordCommand command) {
+ CollaborationRecordModel model = recordModelFactory.create();
+ model.loadFromAddCommand(command);
+ model.saveRecord();
+ saveChildren(model.getRecordId(), command);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void updateRecord(UpdateCollaborationRecordCommand command) {
+ CollaborationRecordModel model = recordModelFactory.loadById(command.getRecordId());
+ model.loadFromUpdateCommand(command);
+ model.updateRecord();
+ replaceChildren(command.getRecordId(), command);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteRecord(BulkOperationCommand command) {
+ Set ids = command.getIds();
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+ taskService.removeByRecordIds(ids);
+ expenditureService.removeByRecordIds(ids);
+ settlementService.removeByRecordIds(ids);
+ fileService.removeByRecordIds(ids);
+ recordService.removeBatchByIds(ids);
+ }
+
+ public List getOptions() {
+ return Arrays.stream(CollaborationOptionEnum.values())
+ .map(item -> new CollaborationOptionDTO(item.getType(), item.getLabel(), Arrays.asList(item.getValues())))
+ .collect(Collectors.toList());
+ }
+
+ public List getMonthlyStatistics(Integer year) {
+ List statistics = new ArrayList<>();
+ for (int month = 1; month <= 12; month++) {
+ statistics.add(buildMonthlyStatistics(year, month));
+ }
+ return statistics;
+ }
+
+ private CollaborationRecordDTO buildRecordDTO(CollaborationRecordEntity entity) {
+ CollaborationRecordDTO dto = new CollaborationRecordDTO(entity);
+ fillRecordStats(dto, entity.getRecordId());
+ return dto;
+ }
+
+ private void fillDetailChildren(CollaborationRecordDetailDTO dto, Long recordId) {
+ dto.setTasks(taskService.listByRecordId(recordId).stream()
+ .map(CollaborationTaskDTO::new)
+ .collect(Collectors.toList()));
+ dto.setExpenditures(expenditureService.listByRecordId(recordId).stream()
+ .map(CollaborationExpenditureDTO::new)
+ .collect(Collectors.toList()));
+ dto.setSettlements(settlementService.listByRecordId(recordId).stream()
+ .map(CollaborationSettlementDTO::new)
+ .collect(Collectors.toList()));
+ dto.setFiles(fileService.listByRecordId(recordId).stream()
+ .map(CollaborationFileDTO::new)
+ .collect(Collectors.toList()));
+ }
+
+ private void fillRecordStats(CollaborationRecordDTO dto, Long recordId) {
+ List tasks = taskService.listByRecordId(recordId);
+ List expenditures = expenditureService.listByRecordId(recordId);
+ List settlements = settlementService.listByRecordId(recordId);
+ dto.setTasksNum(tasks.size());
+ dto.setCompletedTasksNum((int) tasks.stream().filter(item -> item.getReleaseDate() != null).count());
+ dto.setPurchaseSettlementStatus(getStatus(dto.getPurchasePrice(), settlements, PURCHASE_FEE));
+ dto.setDeliverySettlementStatus(getStatus(sumExpenditure(expenditures, DELIVERY_FEE), settlements, DELIVERY_FEE));
+ dto.setRemunerationSettlementStatus(getStatus(dto.getRemuneration(), settlements, REMUNERATION_FEE));
+ }
+
+ private SettlementStatusDTO getStatus(
+ BigDecimal expectedAmount, List settlements, String purpose) {
+ List matched = filterSettlements(settlements, purpose);
+ if (matched.isEmpty()) {
+ return toStatusDTO(SettlementStatusEnum.NONE);
+ }
+ BigDecimal settledAmount = sumSettlement(matched);
+ BigDecimal expected = defaultAmount(expectedAmount);
+ if (isAllSettlementPending(matched)) {
+ return toStatusDTO(SettlementStatusEnum.UNSETTLED);
+ }
+ if (isAllSettlementDue(matched) && settledAmount.compareTo(expected) >= 0) {
+ return toStatusDTO(SettlementStatusEnum.SETTLED);
+ }
+ return toStatusDTO(SettlementStatusEnum.PARTIAL);
+ }
+
+ private List filterSettlements(
+ List settlements, String purpose) {
+ if (settlements == null) {
+ return Collections.emptyList();
+ }
+ return settlements.stream()
+ .filter(item -> purpose.equals(item.getPurpose()))
+ .collect(Collectors.toList());
+ }
+
+ private SettlementStatusDTO toStatusDTO(SettlementStatusEnum status) {
+ return new SettlementStatusDTO(status.getValue(), status.getLabel());
+ }
+
+ private boolean isAllSettlementDue(List settlements) {
+ Date today = getTodayStart();
+ return settlements.stream()
+ .allMatch(item -> item.getSettleDate() != null && !item.getSettleDate().after(today));
+ }
+
+ private boolean isAllSettlementPending(List settlements) {
+ Date today = getTodayStart();
+ return settlements.stream()
+ .allMatch(item -> item.getSettleDate() == null || item.getSettleDate().after(today));
+ }
+
+ private void replaceChildren(Long recordId, AddCollaborationRecordCommand command) {
+ List ids = Collections.singletonList(recordId);
+ taskService.removeByRecordIds(ids);
+ expenditureService.removeByRecordIds(ids);
+ settlementService.removeByRecordIds(ids);
+ fileService.removeByRecordIds(ids);
+ saveChildren(recordId, command);
+ }
+
+ private void saveChildren(Long recordId, AddCollaborationRecordCommand command) {
+ saveTasks(recordId, command.getTasks());
+ saveExpenditures(recordId, command.getExpenditures());
+ saveSettlements(recordId, command.getSettlements());
+ saveFiles(recordId, command.getFiles());
+ }
+
+ private void saveTasks(Long recordId, List tasks) {
+ List entities = toTaskEntities(recordId, tasks);
+ if (!entities.isEmpty()) {
+ taskService.saveBatch(entities);
+ }
+ }
+
+ private void saveExpenditures(Long recordId, List expenditures) {
+ List entities = toExpenditureEntities(recordId, expenditures);
+ if (!entities.isEmpty()) {
+ expenditureService.saveBatch(entities);
+ }
+ }
+
+ private void saveSettlements(Long recordId, List settlements) {
+ List entities = toSettlementEntities(recordId, settlements);
+ if (!entities.isEmpty()) {
+ settlementService.saveBatch(entities);
+ }
+ }
+
+ private void saveFiles(Long recordId, List files) {
+ List entities = toFileEntities(recordId, files);
+ if (!entities.isEmpty()) {
+ fileService.saveBatch(entities);
+ }
+ }
+
+ private List toTaskEntities(Long recordId, List tasks) {
+ List entities = new ArrayList<>();
+ if (tasks == null) {
+ return entities;
+ }
+ for (int i = 0; i < tasks.size(); i++) {
+ entities.add(toTaskEntity(recordId, tasks.get(i), i));
+ }
+ return entities;
+ }
+
+ private CollaborationTaskEntity toTaskEntity(Long recordId, CollaborationTaskCommand command, int index) {
+ CollaborationTaskEntity entity = new CollaborationTaskEntity();
+ entity.setRecordId(recordId);
+ entity.setReleaseDate(command.getReleaseDate());
+ entity.setSortOrder(index);
+ return entity;
+ }
+
+ private List toExpenditureEntities(
+ Long recordId, List expenditures) {
+ if (expenditures == null) {
+ return Collections.emptyList();
+ }
+ return expenditures.stream()
+ .map(item -> toExpenditureEntity(recordId, item))
+ .collect(Collectors.toList());
+ }
+
+ private CollaborationExpenditureEntity toExpenditureEntity(Long recordId, CollaborationExpenditureCommand command) {
+ CollaborationExpenditureEntity entity = new CollaborationExpenditureEntity();
+ entity.setRecordId(recordId);
+ entity.setSpendDate(command.getSpendDate());
+ entity.setAmount(command.getAmount());
+ entity.setPurpose(command.getPurpose());
+ return entity;
+ }
+
+ private List toSettlementEntities(
+ Long recordId, List settlements) {
+ if (settlements == null) {
+ return Collections.emptyList();
+ }
+ return settlements.stream()
+ .map(item -> toSettlementEntity(recordId, item))
+ .collect(Collectors.toList());
+ }
+
+ private CollaborationSettlementEntity toSettlementEntity(Long recordId, CollaborationSettlementCommand command) {
+ CollaborationSettlementEntity entity = new CollaborationSettlementEntity();
+ entity.setRecordId(recordId);
+ entity.setSettleDate(command.getSettleDate());
+ entity.setMethod(command.getMethod());
+ entity.setIncome(command.getIncome());
+ entity.setPurpose(command.getPurpose());
+ return entity;
+ }
+
+ private List toFileEntities(Long recordId, List files) {
+ List entities = new ArrayList<>();
+ if (files == null) {
+ return entities;
+ }
+ for (int i = 0; i < files.size(); i++) {
+ entities.add(toFileEntity(recordId, files.get(i), i));
+ }
+ return entities;
+ }
+
+ private CollaborationFileEntity toFileEntity(Long recordId, CollaborationFileCommand command, int index) {
+ CollaborationFileEntity entity = new CollaborationFileEntity();
+ entity.setRecordId(recordId);
+ entity.setFileType(command.getFileType());
+ entity.setUrl(command.getUrl());
+ entity.setFileName(command.getFileName());
+ entity.setNewFileName(command.getNewFileName());
+ entity.setOriginalFilename(command.getOriginalFilename());
+ entity.setSortOrder(index);
+ return entity;
+ }
+
+ private CollaborationMonthlyStatisticsDTO buildMonthlyStatistics(Integer year, Integer month) {
+ Date[] range = getMonthRange(year, month);
+ BigDecimal purchasePrice = sumPurchasePrice(range);
+ BigDecimal expenditureAmount = sumExpenditure(range);
+ BigDecimal settledRemuneration = sumSettlement(range, REMUNERATION_FEE);
+ BigDecimal settledTotal = sumSettlement(range, null);
+ return new CollaborationMonthlyStatisticsDTO(month, purchasePrice, expenditureAmount,
+ settledRemuneration, settledTotal);
+ }
+
+ private BigDecimal sumPurchasePrice(Date[] range) {
+ List records = recordService.list(new QueryWrapper()
+ .ge("purchase_date", range[0])
+ .le("purchase_date", range[1]));
+ return records.stream()
+ .map(CollaborationRecordEntity::getPurchasePrice)
+ .map(this::defaultAmount)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ }
+
+ private BigDecimal sumExpenditure(Date[] range) {
+ List expenditures = expenditureService.list(
+ new QueryWrapper().ge("spend_date", range[0]).le("spend_date", range[1]));
+ return expenditures.stream()
+ .map(CollaborationExpenditureEntity::getAmount)
+ .map(this::defaultAmount)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ }
+
+ private BigDecimal sumSettlement(Date[] range, String purpose) {
+ QueryWrapper wrapper = new QueryWrapper()
+ .ge("settle_date", range[0])
+ .le("settle_date", range[1])
+ .eq(purpose != null, "purpose", purpose);
+ return sumSettlement(settlementService.list(wrapper));
+ }
+
+ private BigDecimal sumExpenditure(List expenditures, String purpose) {
+ return expenditures.stream()
+ .filter(item -> purpose.equals(item.getPurpose()))
+ .map(CollaborationExpenditureEntity::getAmount)
+ .map(this::defaultAmount)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ }
+
+ private BigDecimal sumSettlement(List settlements) {
+ return settlements.stream()
+ .map(CollaborationSettlementEntity::getIncome)
+ .map(this::defaultAmount)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ }
+
+ private BigDecimal defaultAmount(BigDecimal amount) {
+ return amount == null ? BigDecimal.ZERO : amount;
+ }
+
+ private Date[] getMonthRange(Integer year, Integer month) {
+ Calendar start = Calendar.getInstance();
+ start.set(year, month - 1, 1, 0, 0, 0);
+ start.set(Calendar.MILLISECOND, 0);
+ Calendar end = (Calendar) start.clone();
+ end.add(Calendar.MONTH, 1);
+ end.add(Calendar.MILLISECOND, -1);
+ return new Date[]{start.getTime(), end.getTime()};
+ }
+
+ private Date getTodayStart() {
+ Calendar today = Calendar.getInstance();
+ today.set(Calendar.HOUR_OF_DAY, 0);
+ today.set(Calendar.MINUTE, 0);
+ today.set(Calendar.SECOND, 0);
+ today.set(Calendar.MILLISECOND, 0);
+ return today.getTime();
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/AddCollaborationRecordCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/AddCollaborationRecordCommand.java
new file mode 100644
index 0000000..3537d66
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/AddCollaborationRecordCommand.java
@@ -0,0 +1,83 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.PositiveOrZero;
+import javax.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * @author codex
+ */
+@Data
+public class AddCollaborationRecordCommand {
+
+ @NotBlank(message = "品牌不能为空")
+ @Size(max = 100, message = "品牌长度不能超过100个字符")
+ protected String brand;
+
+ @NotBlank(message = "物品不能为空")
+ @Size(max = 100, message = "物品长度不能超过100个字符")
+ protected String goods;
+
+ @Size(max = 50, message = "合作平台长度不能超过50个字符")
+ protected String cooperationPlatform;
+
+ @NotNull(message = "返图数量不能为空")
+ @Min(value = 1, message = "返图数量至少为1")
+ protected Integer imageReturnNum;
+
+ @Size(max = 30, message = "留存方式长度不能超过30个字符")
+ protected String retainedMethod;
+
+ @Size(max = 30, message = "合作方式长度不能超过30个字符")
+ protected String cooperatedMethod;
+
+ @Size(max = 30, message = "购入方式长度不能超过30个字符")
+ protected String purchaseMethod;
+
+ @PositiveOrZero(message = "购入金额不能小于0")
+ protected BigDecimal purchasePrice;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ protected Date purchaseDate;
+
+ @Size(max = 30, message = "购入平台长度不能超过30个字符")
+ protected String purchasePlatform;
+
+ @NotNull(message = "预完成日期不能为空")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ protected Date deadline;
+
+ @PositiveOrZero(message = "稿费不能小于0")
+ protected BigDecimal remuneration;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ protected Date completeDate;
+
+ @Size(max = 1000, message = "拍摄要求长度不能超过1000个字符")
+ protected String requirements;
+
+ @Size(max = 1000, message = "备注长度不能超过1000个字符")
+ protected String remark;
+
+ @Valid
+ protected List tasks = new ArrayList<>();
+
+ @Valid
+ protected List expenditures = new ArrayList<>();
+
+ @Valid
+ protected List settlements = new ArrayList<>();
+
+ @Valid
+ protected List files = new ArrayList<>();
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationExpenditureCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationExpenditureCommand.java
new file mode 100644
index 0000000..d34b8ac
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationExpenditureCommand.java
@@ -0,0 +1,25 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.Date;
+import javax.validation.constraints.PositiveOrZero;
+import javax.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * @author codex
+ */
+@Data
+public class CollaborationExpenditureCommand {
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date spendDate;
+
+ @PositiveOrZero(message = "支出金额不能小于0")
+ private BigDecimal amount;
+
+ @Size(max = 30, message = "支出用途长度不能超过30个字符")
+ private String purpose;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationFileCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationFileCommand.java
new file mode 100644
index 0000000..0607aa6
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationFileCommand.java
@@ -0,0 +1,30 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * @author codex
+ */
+@Data
+public class CollaborationFileCommand {
+
+ @NotBlank(message = "文件类型不能为空")
+ @Size(max = 30, message = "文件类型长度不能超过30个字符")
+ private String fileType;
+
+ @NotBlank(message = "文件地址不能为空")
+ @Size(max = 500, message = "文件地址长度不能超过500个字符")
+ private String url;
+
+ @Size(max = 300, message = "文件路径长度不能超过300个字符")
+ private String fileName;
+
+ @Size(max = 200, message = "服务端文件名长度不能超过200个字符")
+ private String newFileName;
+
+ @Size(max = 300, message = "原始文件名长度不能超过300个字符")
+ private String originalFilename;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationSettlementCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationSettlementCommand.java
new file mode 100644
index 0000000..067c433
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationSettlementCommand.java
@@ -0,0 +1,28 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.Date;
+import javax.validation.constraints.PositiveOrZero;
+import javax.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * @author codex
+ */
+@Data
+public class CollaborationSettlementCommand {
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date settleDate;
+
+ @Size(max = 30, message = "结款方式长度不能超过30个字符")
+ private String method;
+
+ @PositiveOrZero(message = "结款金额不能小于0")
+ private BigDecimal income;
+
+ @Size(max = 30, message = "结款用途长度不能超过30个字符")
+ private String purpose;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationTaskCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationTaskCommand.java
new file mode 100644
index 0000000..b47a0a6
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/CollaborationTaskCommand.java
@@ -0,0 +1,16 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * @author codex
+ */
+@Data
+public class CollaborationTaskCommand {
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date releaseDate;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/UpdateCollaborationRecordCommand.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/UpdateCollaborationRecordCommand.java
new file mode 100644
index 0000000..d5f8922
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/command/UpdateCollaborationRecordCommand.java
@@ -0,0 +1,19 @@
+package com.agileboot.domain.collaboration.record.command;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Positive;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author codex
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class UpdateCollaborationRecordCommand extends AddCollaborationRecordCommand {
+
+ @NotNull(message = "合作记录ID不能为空")
+ @Positive
+ private Long recordId;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureEntity.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureEntity.java
new file mode 100644
index 0000000..fb86625
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureEntity.java
@@ -0,0 +1,46 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.agileboot.common.core.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author codex
+ */
+@Getter
+@Setter
+@TableName("collaboration_expenditure")
+@ApiModel(value = "CollaborationExpenditureEntity对象", description = "合作支出表")
+public class CollaborationExpenditureEntity extends BaseEntity {
+
+ @TableId(value = "expenditure_id", type = IdType.AUTO)
+ private Long expenditureId;
+
+ @TableField("record_id")
+ private Long recordId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @TableField("spend_date")
+ private Date spendDate;
+
+ @TableField("amount")
+ private BigDecimal amount;
+
+ @TableField("purpose")
+ private String purpose;
+
+ @Override
+ public Serializable pkVal() {
+ return this.expenditureId;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureMapper.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureMapper.java
new file mode 100644
index 0000000..54c29f4
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureMapper.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author codex
+ */
+public interface CollaborationExpenditureMapper extends BaseMapper {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureService.java
new file mode 100644
index 0000000..666026a
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureService.java
@@ -0,0 +1,16 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author codex
+ */
+public interface CollaborationExpenditureService extends IService {
+
+ List listByRecordId(Long recordId);
+
+ void removeByRecordIds(Collection recordIds);
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureServiceImpl.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureServiceImpl.java
new file mode 100644
index 0000000..4dd2df5
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationExpenditureServiceImpl.java
@@ -0,0 +1,36 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author codex
+ */
+@Service
+public class CollaborationExpenditureServiceImpl
+ extends ServiceImpl
+ implements CollaborationExpenditureService {
+
+ @Override
+ public List listByRecordId(Long recordId) {
+ if (recordId == null) {
+ return Collections.emptyList();
+ }
+ return list(new QueryWrapper()
+ .eq("record_id", recordId)
+ .orderByAsc("spend_date"));
+ }
+
+ @Override
+ public void removeByRecordIds(Collection recordIds) {
+ if (recordIds == null || recordIds.isEmpty()) {
+ return;
+ }
+ remove(new QueryWrapper().in("record_id", recordIds));
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileEntity.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileEntity.java
new file mode 100644
index 0000000..d0f0880
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileEntity.java
@@ -0,0 +1,51 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.agileboot.common.core.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author codex
+ */
+@Getter
+@Setter
+@TableName("collaboration_file")
+@ApiModel(value = "CollaborationFileEntity对象", description = "合作文件表")
+public class CollaborationFileEntity extends BaseEntity {
+
+ @TableId(value = "file_id", type = IdType.AUTO)
+ private Long fileId;
+
+ @TableField("record_id")
+ private Long recordId;
+
+ @TableField("file_type")
+ private String fileType;
+
+ @TableField("url")
+ private String url;
+
+ @TableField("file_name")
+ private String fileName;
+
+ @TableField("new_file_name")
+ private String newFileName;
+
+ @TableField("original_filename")
+ private String originalFilename;
+
+ @TableField("sort_order")
+ private Integer sortOrder;
+
+ @Override
+ public Serializable pkVal() {
+ return this.fileId;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileMapper.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileMapper.java
new file mode 100644
index 0000000..f409e6f
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileMapper.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author codex
+ */
+public interface CollaborationFileMapper extends BaseMapper {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileService.java
new file mode 100644
index 0000000..844c914
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileService.java
@@ -0,0 +1,16 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author codex
+ */
+public interface CollaborationFileService extends IService {
+
+ List listByRecordId(Long recordId);
+
+ void removeByRecordIds(Collection recordIds);
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileServiceImpl.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileServiceImpl.java
new file mode 100644
index 0000000..9de15e5
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationFileServiceImpl.java
@@ -0,0 +1,36 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author codex
+ */
+@Service
+public class CollaborationFileServiceImpl
+ extends ServiceImpl
+ implements CollaborationFileService {
+
+ @Override
+ public List listByRecordId(Long recordId) {
+ if (recordId == null) {
+ return Collections.emptyList();
+ }
+ return list(new QueryWrapper()
+ .eq("record_id", recordId)
+ .orderByAsc("file_type", "sort_order"));
+ }
+
+ @Override
+ public void removeByRecordIds(Collection recordIds) {
+ if (recordIds == null || recordIds.isEmpty()) {
+ return;
+ }
+ remove(new QueryWrapper().in("record_id", recordIds));
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordEntity.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordEntity.java
new file mode 100644
index 0000000..a6bdbf7
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordEntity.java
@@ -0,0 +1,100 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.agileboot.common.core.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author codex
+ */
+@Getter
+@Setter
+@TableName("collaboration_record")
+@ApiModel(value = "CollaborationRecordEntity对象", description = "合作记录表")
+public class CollaborationRecordEntity extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("合作记录ID")
+ @TableId(value = "record_id", type = IdType.AUTO)
+ private Long recordId;
+
+ @ApiModelProperty("品牌")
+ @TableField("brand")
+ private String brand;
+
+ @ApiModelProperty("物品")
+ @TableField("goods")
+ private String goods;
+
+ @ApiModelProperty("合作平台")
+ @TableField("cooperation_platform")
+ private String cooperationPlatform;
+
+ @ApiModelProperty("返图数量")
+ @TableField("image_return_num")
+ private Integer imageReturnNum;
+
+ @ApiModelProperty("留存方式")
+ @TableField("retained_method")
+ private String retainedMethod;
+
+ @ApiModelProperty("合作方式")
+ @TableField("cooperated_method")
+ private String cooperatedMethod;
+
+ @ApiModelProperty("购入方式")
+ @TableField("purchase_method")
+ private String purchaseMethod;
+
+ @ApiModelProperty("购入金额")
+ @TableField("purchase_price")
+ private BigDecimal purchasePrice;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @ApiModelProperty("购入日期")
+ @TableField("purchase_date")
+ private Date purchaseDate;
+
+ @ApiModelProperty("购入平台")
+ @TableField("purchase_platform")
+ private String purchasePlatform;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @ApiModelProperty("预完成日期")
+ @TableField("deadline")
+ private Date deadline;
+
+ @ApiModelProperty("稿费")
+ @TableField("remuneration")
+ private BigDecimal remuneration;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @ApiModelProperty("完成日期")
+ @TableField("complete_date")
+ private Date completeDate;
+
+ @ApiModelProperty("拍摄要求")
+ @TableField("requirements")
+ private String requirements;
+
+ @ApiModelProperty("备注")
+ @TableField("remark")
+ private String remark;
+
+ @Override
+ public Serializable pkVal() {
+ return this.recordId;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordMapper.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordMapper.java
new file mode 100644
index 0000000..930a95a
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordMapper.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author codex
+ */
+public interface CollaborationRecordMapper extends BaseMapper {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordService.java
new file mode 100644
index 0000000..cb6c1f3
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordService.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @author codex
+ */
+public interface CollaborationRecordService extends IService {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordServiceImpl.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordServiceImpl.java
new file mode 100644
index 0000000..da1b3ea
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationRecordServiceImpl.java
@@ -0,0 +1,14 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author codex
+ */
+@Service
+public class CollaborationRecordServiceImpl
+ extends ServiceImpl
+ implements CollaborationRecordService {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementEntity.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementEntity.java
new file mode 100644
index 0000000..cbe013a
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementEntity.java
@@ -0,0 +1,49 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.agileboot.common.core.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author codex
+ */
+@Getter
+@Setter
+@TableName("collaboration_settlement")
+@ApiModel(value = "CollaborationSettlementEntity对象", description = "合作结款表")
+public class CollaborationSettlementEntity extends BaseEntity {
+
+ @TableId(value = "settlement_id", type = IdType.AUTO)
+ private Long settlementId;
+
+ @TableField("record_id")
+ private Long recordId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @TableField("settle_date")
+ private Date settleDate;
+
+ @TableField("method")
+ private String method;
+
+ @TableField("income")
+ private BigDecimal income;
+
+ @TableField("purpose")
+ private String purpose;
+
+ @Override
+ public Serializable pkVal() {
+ return this.settlementId;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementMapper.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementMapper.java
new file mode 100644
index 0000000..fa2c9c5
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementMapper.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author codex
+ */
+public interface CollaborationSettlementMapper extends BaseMapper {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementService.java
new file mode 100644
index 0000000..2719399
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementService.java
@@ -0,0 +1,16 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author codex
+ */
+public interface CollaborationSettlementService extends IService {
+
+ List listByRecordId(Long recordId);
+
+ void removeByRecordIds(Collection recordIds);
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementServiceImpl.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementServiceImpl.java
new file mode 100644
index 0000000..eff8d0e
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationSettlementServiceImpl.java
@@ -0,0 +1,36 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author codex
+ */
+@Service
+public class CollaborationSettlementServiceImpl
+ extends ServiceImpl
+ implements CollaborationSettlementService {
+
+ @Override
+ public List listByRecordId(Long recordId) {
+ if (recordId == null) {
+ return Collections.emptyList();
+ }
+ return list(new QueryWrapper()
+ .eq("record_id", recordId)
+ .orderByAsc("settle_date"));
+ }
+
+ @Override
+ public void removeByRecordIds(Collection recordIds) {
+ if (recordIds == null || recordIds.isEmpty()) {
+ return;
+ }
+ remove(new QueryWrapper().in("record_id", recordIds));
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskEntity.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskEntity.java
new file mode 100644
index 0000000..07c8a2f
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskEntity.java
@@ -0,0 +1,42 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.agileboot.common.core.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author codex
+ */
+@Getter
+@Setter
+@TableName("collaboration_task")
+@ApiModel(value = "CollaborationTaskEntity对象", description = "合作笔记任务表")
+public class CollaborationTaskEntity extends BaseEntity {
+
+ @TableId(value = "task_id", type = IdType.AUTO)
+ private Long taskId;
+
+ @TableField("record_id")
+ private Long recordId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @TableField("release_date")
+ private Date releaseDate;
+
+ @TableField("sort_order")
+ private Integer sortOrder;
+
+ @Override
+ public Serializable pkVal() {
+ return this.taskId;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskMapper.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskMapper.java
new file mode 100644
index 0000000..bc6ab7a
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskMapper.java
@@ -0,0 +1,10 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author codex
+ */
+public interface CollaborationTaskMapper extends BaseMapper {
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskService.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskService.java
new file mode 100644
index 0000000..59b5994
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskService.java
@@ -0,0 +1,16 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author codex
+ */
+public interface CollaborationTaskService extends IService {
+
+ List listByRecordId(Long recordId);
+
+ void removeByRecordIds(Collection recordIds);
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskServiceImpl.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskServiceImpl.java
new file mode 100644
index 0000000..3a55661
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/db/CollaborationTaskServiceImpl.java
@@ -0,0 +1,36 @@
+package com.agileboot.domain.collaboration.record.db;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author codex
+ */
+@Service
+public class CollaborationTaskServiceImpl
+ extends ServiceImpl
+ implements CollaborationTaskService {
+
+ @Override
+ public List listByRecordId(Long recordId) {
+ if (recordId == null) {
+ return Collections.emptyList();
+ }
+ return list(new QueryWrapper()
+ .eq("record_id", recordId)
+ .orderByAsc("sort_order"));
+ }
+
+ @Override
+ public void removeByRecordIds(Collection recordIds) {
+ if (recordIds == null || recordIds.isEmpty()) {
+ return;
+ }
+ remove(new QueryWrapper().in("record_id", recordIds));
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationExpenditureDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationExpenditureDTO.java
new file mode 100644
index 0000000..274903e
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationExpenditureDTO.java
@@ -0,0 +1,33 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationExpenditureEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+@Data
+public class CollaborationExpenditureDTO {
+
+ public CollaborationExpenditureDTO(CollaborationExpenditureEntity entity) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ }
+
+ private Long expenditureId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date spendDate;
+
+ private BigDecimal amount;
+
+ private String purpose;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationFileDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationFileDTO.java
new file mode 100644
index 0000000..5e213a3
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationFileDTO.java
@@ -0,0 +1,35 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationFileEntity;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+@Data
+public class CollaborationFileDTO {
+
+ public CollaborationFileDTO(CollaborationFileEntity entity) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ }
+
+ private Long fileId;
+
+ private String fileType;
+
+ private String url;
+
+ private String fileName;
+
+ private String newFileName;
+
+ private String originalFilename;
+
+ private Integer sortOrder;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationMonthlyStatisticsDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationMonthlyStatisticsDTO.java
new file mode 100644
index 0000000..888de96
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationMonthlyStatisticsDTO.java
@@ -0,0 +1,26 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import java.math.BigDecimal;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class CollaborationMonthlyStatisticsDTO {
+
+ private Integer month;
+
+ private BigDecimal purchasePrice;
+
+ private BigDecimal expenditureAmount;
+
+ private BigDecimal settledRemuneration;
+
+ private BigDecimal settledTotal;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationOptionDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationOptionDTO.java
new file mode 100644
index 0000000..3f1fb62
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationOptionDTO.java
@@ -0,0 +1,22 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class CollaborationOptionDTO {
+
+ private String type;
+
+ private String label;
+
+ private List values;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDTO.java
new file mode 100644
index 0000000..59db750
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDTO.java
@@ -0,0 +1,71 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+@Data
+public class CollaborationRecordDTO {
+
+ public CollaborationRecordDTO(CollaborationRecordEntity entity) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ }
+
+ private Long recordId;
+
+ private String brand;
+
+ private String goods;
+
+ private String cooperationPlatform;
+
+ private Integer imageReturnNum;
+
+ private String retainedMethod;
+
+ private String cooperatedMethod;
+
+ private String purchaseMethod;
+
+ private BigDecimal purchasePrice;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date purchaseDate;
+
+ private String purchasePlatform;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date deadline;
+
+ private BigDecimal remuneration;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date completeDate;
+
+ private String requirements;
+
+ private String remark;
+
+ private Integer tasksNum;
+
+ private Integer completedTasksNum;
+
+ private SettlementStatusDTO purchaseSettlementStatus;
+
+ private SettlementStatusDTO deliverySettlementStatus;
+
+ private SettlementStatusDTO remunerationSettlementStatus;
+
+ private Date createTime;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDetailDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDetailDTO.java
new file mode 100644
index 0000000..8cb5f67
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationRecordDetailDTO.java
@@ -0,0 +1,30 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@Data
+public class CollaborationRecordDetailDTO extends CollaborationRecordDTO {
+
+ public CollaborationRecordDetailDTO(CollaborationRecordEntity entity) {
+ super(entity);
+ }
+
+ private List tasks = new ArrayList<>();
+
+ private List expenditures = new ArrayList<>();
+
+ private List settlements = new ArrayList<>();
+
+ private List files = new ArrayList<>();
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationSettlementDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationSettlementDTO.java
new file mode 100644
index 0000000..adbc570
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationSettlementDTO.java
@@ -0,0 +1,35 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationSettlementEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
+import java.util.Date;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+@Data
+public class CollaborationSettlementDTO {
+
+ public CollaborationSettlementDTO(CollaborationSettlementEntity entity) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ }
+
+ private Long settlementId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date settleDate;
+
+ private String method;
+
+ private BigDecimal income;
+
+ private String purpose;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationTaskDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationTaskDTO.java
new file mode 100644
index 0000000..de0aee7
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/CollaborationTaskDTO.java
@@ -0,0 +1,30 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationTaskEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.util.Date;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+@Data
+public class CollaborationTaskDTO {
+
+ public CollaborationTaskDTO(CollaborationTaskEntity entity) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ }
+
+ private Long taskId;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date releaseDate;
+
+ private Integer sortOrder;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/SettlementStatusDTO.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/SettlementStatusDTO.java
new file mode 100644
index 0000000..24484dc
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/dto/SettlementStatusDTO.java
@@ -0,0 +1,19 @@
+package com.agileboot.domain.collaboration.record.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SettlementStatusDTO {
+
+ private String status;
+
+ private String label;
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationFileTypeEnum.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationFileTypeEnum.java
new file mode 100644
index 0000000..6efcffc
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationFileTypeEnum.java
@@ -0,0 +1,12 @@
+package com.agileboot.domain.collaboration.record.enumtype;
+
+/**
+ * @author codex
+ */
+public enum CollaborationFileTypeEnum {
+
+ GOODS_IMAGE,
+
+ ATTACHMENT
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationOptionEnum.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationOptionEnum.java
new file mode 100644
index 0000000..9cc8897
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/CollaborationOptionEnum.java
@@ -0,0 +1,45 @@
+package com.agileboot.domain.collaboration.record.enumtype;
+
+import lombok.Getter;
+
+/**
+ * @author codex
+ */
+@Getter
+public enum CollaborationOptionEnum {
+
+ COOPERATION_PLATFORM("cooperationPlatform", "合作平台",
+ new String[]{"小红书", "得物", "抖音", "淘宝", "京东", "微博", "B站", "其他"}),
+
+ RETAINED_METHOD("retainedMethod", "留存方式",
+ new String[]{"寄拍", "送拍", "置换", "无需寄样"}),
+
+ COOPERATED_METHOD("cooperatedMethod", "合作方式",
+ new String[]{"水下", "蒲公英"}),
+
+ PURCHASE_METHOD("purchaseMethod", "购入方式",
+ new String[]{"拍单", "商家寄出"}),
+
+ PURCHASE_PLATFORM("purchasePlatform", "购入平台",
+ new String[]{"淘宝", "小红书", "京东", "抖音"}),
+
+ EXPENDITURE_PURPOSE("expenditurePurpose", "支出用途",
+ new String[]{"快递费用", "返点", "蒲公英扣税费用"}),
+
+ SETTLEMENT_METHOD("settlementMethod", "结款方式",
+ new String[]{"微信", "支付宝", "蒲公英", "京灵平台"}),
+
+ SETTLEMENT_PURPOSE("settlementPurpose", "结款用途",
+ new String[]{"稿费", "快递费用", "拍单费用", "蒲公英扣税费用"});
+
+ private final String type;
+ private final String label;
+ private final String[] values;
+
+ CollaborationOptionEnum(String type, String label, String[] values) {
+ this.type = type;
+ this.label = label;
+ this.values = values;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/SettlementStatusEnum.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/SettlementStatusEnum.java
new file mode 100644
index 0000000..f36566e
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/enumtype/SettlementStatusEnum.java
@@ -0,0 +1,27 @@
+package com.agileboot.domain.collaboration.record.enumtype;
+
+import lombok.Getter;
+
+/**
+ * @author codex
+ */
+@Getter
+public enum SettlementStatusEnum {
+
+ NONE("NONE", "无结款项"),
+
+ SETTLED("SETTLED", "已结"),
+
+ UNSETTLED("UNSETTLED", "未结"),
+
+ PARTIAL("PARTIAL", "未结清");
+
+ private final String value;
+ private final String label;
+
+ SettlementStatusEnum(String value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModel.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModel.java
new file mode 100644
index 0000000..7e53f0a
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModel.java
@@ -0,0 +1,79 @@
+package com.agileboot.domain.collaboration.record.model;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.agileboot.common.exception.ApiException;
+import com.agileboot.common.exception.error.ErrorCode;
+import com.agileboot.domain.collaboration.record.command.AddCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.command.UpdateCollaborationRecordCommand;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordService;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author codex
+ */
+@NoArgsConstructor
+public class CollaborationRecordModel extends CollaborationRecordEntity {
+
+ private CollaborationRecordService recordService;
+
+ public CollaborationRecordModel(CollaborationRecordService recordService) {
+ this.recordService = recordService;
+ }
+
+ public CollaborationRecordModel(CollaborationRecordEntity entity, CollaborationRecordService recordService) {
+ if (entity != null) {
+ BeanUtil.copyProperties(entity, this);
+ }
+ this.recordService = recordService;
+ }
+
+ public void loadFromAddCommand(AddCollaborationRecordCommand command) {
+ if (command == null) {
+ return;
+ }
+ BeanUtil.copyProperties(command, this, "recordId");
+ loadDefaultValues();
+ }
+
+ public void loadFromUpdateCommand(UpdateCollaborationRecordCommand command) {
+ if (command == null) {
+ return;
+ }
+ loadFromAddCommand(command);
+ }
+
+ public void checkRequiredFields() {
+ if (StrUtil.isBlank(getBrand())) {
+ throw new ApiException(ErrorCode.FAILED);
+ }
+ if (StrUtil.isBlank(getGoods())) {
+ throw new ApiException(ErrorCode.FAILED);
+ }
+ }
+
+ public void saveRecord() {
+ recordService.save(this);
+ }
+
+ public void updateRecord() {
+ recordService.updateById(this);
+ }
+
+ private void loadDefaultValues() {
+ if (getImageReturnNum() == null) {
+ setImageReturnNum(1);
+ }
+ if (StrUtil.isBlank(getRetainedMethod())) {
+ setRetainedMethod("寄拍");
+ }
+ if (StrUtil.isBlank(getCooperatedMethod())) {
+ setCooperatedMethod("水下");
+ }
+ if (StrUtil.isBlank(getPurchaseMethod())) {
+ setPurchaseMethod("拍单");
+ }
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModelFactory.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModelFactory.java
new file mode 100644
index 0000000..eb9cb0c
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/model/CollaborationRecordModelFactory.java
@@ -0,0 +1,31 @@
+package com.agileboot.domain.collaboration.record.model;
+
+import com.agileboot.common.exception.ApiException;
+import com.agileboot.common.exception.error.ErrorCode.Business;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author codex
+ */
+@Component
+@RequiredArgsConstructor
+public class CollaborationRecordModelFactory {
+
+ private final CollaborationRecordService recordService;
+
+ public CollaborationRecordModel loadById(Long recordId) {
+ CollaborationRecordEntity entity = recordService.getById(recordId);
+ if (entity == null) {
+ throw new ApiException(Business.COMMON_OBJECT_NOT_FOUND, recordId, "合作记录");
+ }
+ return new CollaborationRecordModel(entity, recordService);
+ }
+
+ public CollaborationRecordModel create() {
+ return new CollaborationRecordModel(recordService);
+ }
+
+}
diff --git a/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/query/CollaborationRecordQuery.java b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/query/CollaborationRecordQuery.java
new file mode 100644
index 0000000..d8df66c
--- /dev/null
+++ b/backend/agileboot-domain/src/main/java/com/agileboot/domain/collaboration/record/query/CollaborationRecordQuery.java
@@ -0,0 +1,49 @@
+package com.agileboot.domain.collaboration.record.query;
+
+import cn.hutool.core.util.StrUtil;
+import com.agileboot.common.core.page.AbstractPageQuery;
+import com.agileboot.common.utils.time.DatePickUtil;
+import com.agileboot.domain.collaboration.record.db.CollaborationRecordEntity;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.util.Date;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author codex
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CollaborationRecordQuery extends AbstractPageQuery {
+
+ private String brand;
+
+ private String goods;
+
+ private String cooperationPlatform;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date purchaseBeginTime;
+
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date purchaseEndTime;
+
+ @Override
+ public QueryWrapper addQueryCondition() {
+ QueryWrapper queryWrapper = new QueryWrapper()
+ .like(StrUtil.isNotEmpty(brand), "brand", brand)
+ .like(StrUtil.isNotEmpty(goods), "goods", goods)
+ .eq(StrUtil.isNotEmpty(cooperationPlatform), "cooperation_platform", cooperationPlatform)
+ .ge(purchaseBeginTime != null, "purchase_date", DatePickUtil.getBeginOfTheDay(purchaseBeginTime))
+ .le(purchaseEndTime != null, "purchase_date", DatePickUtil.getEndOfTheDay(purchaseEndTime));
+
+ if (StrUtil.isEmpty(this.getOrderColumn())) {
+ this.setOrderColumn("deadline");
+ this.setOrderDirection("descending");
+ }
+ this.setTimeRangeColumn("deadline");
+ return queryWrapper;
+ }
+
+}
diff --git a/backend/bin/ag-admin.bat b/backend/bin/ag-admin.bat
deleted file mode 100644
index c5193e0..0000000
--- a/backend/bin/ag-admin.bat
+++ /dev/null
@@ -1,67 +0,0 @@
-@echo off
-
-rem jar平级目录
-set AppName=agileboot-admin.jar
-
-rem JVM参数
-set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
-
-
-ECHO.
- ECHO. [1] 启动%AppName%
- ECHO. [2] 关闭%AppName%
- ECHO. [3] 重启%AppName%
- ECHO. [4] 启动状态 %AppName%
- ECHO. [5] 退 出
-ECHO.
-
-ECHO.请输入选择项目的序号:
-set /p ID=
- IF "%id%"=="1" GOTO start
- IF "%id%"=="2" GOTO stop
- IF "%id%"=="3" GOTO restart
- IF "%id%"=="4" GOTO status
- IF "%id%"=="5" EXIT
-PAUSE
-:start
- for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
- set pid=%%a
- set image_name=%%b
- )
- if defined pid (
- echo %%is running
- PAUSE
- )
-
-start javaw %JVM_OPTS% -jar %AppName%
-
-echo starting……
-echo Start %AppName% success...
-goto:eof
-
-rem 函数stop通过jps命令查找pid并结束进程
-:stop
- for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
- set pid=%%a
- set image_name=%%b
- )
- if not defined pid (echo process %AppName% does not exists) else (
- echo prepare to kill %image_name%
- echo start kill %pid% ...
- rem 根据进程ID,kill进程
- taskkill /f /pid %pid%
- )
-goto:eof
-:restart
- call :stop
- call :start
-goto:eof
-:status
- for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
- set pid=%%a
- set image_name=%%b
- )
- if not defined pid (echo process %AppName% is dead ) else (
- echo %image_name% is running
- )
-goto:eof
diff --git a/backend/bin/ag-admin.sh b/backend/bin/ag-admin.sh
deleted file mode 100644
index e6feaf7..0000000
--- a/backend/bin/ag-admin.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/sh
-# ./ag-admin.sh start 启动 stop 停止 restart 重启 status 状态
-AppName=agileboot-admin.jar
-
-# JVM参数
-JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
-APP_HOME=`pwd`
-LOG_PATH=$APP_HOME/logs/$AppName.log
-
-if [ "$1" = "" ];
-then
- echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m"
- exit 1
-fi
-
-if [ "$AppName" = "" ];
-then
- echo -e "\033[0;31m 未输入应用名 \033[0m"
- exit 1
-fi
-
-function start()
-{
- PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
-
- if [ x"$PID" != x"" ]; then
- echo "$AppName is running..."
- else
- nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &
- echo "Start $AppName success..."
- fi
-}
-
-function stop()
-{
- echo "Stop $AppName"
-
- PID=""
- query(){
- PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
- }
-
- query
- if [ x"$PID" != x"" ]; then
- kill -TERM $PID
- echo "$AppName (pid:$PID) exiting..."
- while [ x"$PID" != x"" ]
- do
- sleep 1
- query
- done
- echo "$AppName exited."
- else
- echo "$AppName already stopped."
- fi
-}
-
-function restart()
-{
- stop
- sleep 2
- start
-}
-
-function status()
-{
- PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l`
- if [ $PID != 0 ];then
- echo "$AppName is running..."
- else
- echo "$AppName is not running..."
- fi
-}
-
-case $1 in
- start)
- start;;
- stop)
- stop;;
- restart)
- restart;;
- status)
- status;;
- *)
-
-esac
diff --git a/backend/bin/run.bat b/backend/bin/run.bat
deleted file mode 100644
index 5ae8c92..0000000
--- a/backend/bin/run.bat
+++ /dev/null
@@ -1,14 +0,0 @@
-@echo off
-echo.
-echo [信息] 使用Jar命令运行Web工程。
-echo.
-
-cd %~dp0
-cd ../agileboot-admin/target
-
-set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
-
-java -jar %JAVA_OPTS% agileboot-admin.jar
-
-cd bin
-pause
diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml
index 170bd90..9268e7b 100644
--- a/backend/docker-compose.yml
+++ b/backend/docker-compose.yml
@@ -1,34 +1,38 @@
services:
mysql:
image: mysql:8.0
- container_name: mysql-server
+ container_name: todo-mysql-server
restart: always
+ command:
+ - --character-set-server=utf8mb4
+ - --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: root123
- MYSQL_DATABASE: agileboot_pure
+ MYSQL_DATABASE: todo_agileboot_pure
ports:
- - "3306:3306"
+ - "33061:3306"
volumes:
- - mysql_data:/var/lib/mysql
+ - todo_mysql_data:/var/lib/mysql
+ - ./sql/agileboot.sql:/docker-entrypoint-initdb.d/01-agileboot.sql:ro
networks:
- - db-network
+ - todo-db-network
redis:
image: redis:7.2-alpine
- container_name: redis-server
+ container_name: todo-redis-server
restart: always
command: redis-server --requirepass redis123
ports:
- - "6379:6379"
+ - "63791:6379"
volumes:
- - redis_data:/data
+ - todo_redis_data:/data
networks:
- - db-network
+ - todo-db-network
volumes:
- mysql_data:
- redis_data:
+ todo_mysql_data:
+ todo_redis_data:
networks:
- db-network:
+ todo-db-network:
driver: bridge
diff --git a/backend/mvnw b/backend/mvnw
old mode 100644
new mode 100755
diff --git a/backend/sql/agileboot-20230814.sql b/backend/sql/agileboot.sql
similarity index 76%
rename from backend/sql/agileboot-20230814.sql
rename to backend/sql/agileboot.sql
index 2b6e222..a53883b 100644
--- a/backend/sql/agileboot-20230814.sql
+++ b/backend/sql/agileboot.sql
@@ -1,3 +1,8 @@
+SET NAMES utf8mb4;
+SET character_set_client = utf8mb4;
+SET character_set_connection = utf8mb4;
+SET character_set_results = utf8mb4;
+
create table sys_config
(
config_id int auto_increment comment '参数主键'
@@ -101,68 +106,66 @@ create table sys_menu
INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (1, '系统管理', 2, '', 0, '/system', 0, '', '{"title":"系统管理","icon":"ep:management","showParent":true,"rank":1}', 1, '系统管理目录', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:08:50', 0);
INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (2, '系统监控', 2, '', 0, '/monitor', 0, '', '{"title":"系统监控","icon":"ep:monitor","showParent":true,"rank":3}', 1, '系统监控目录', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:09:15', 0);
INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3, '系统工具', 2, '', 0, '/tool', 0, '', '{"title":"系统工具","icon":"ep:tools","showParent":true,"rank":2}', 1, '系统工具目录', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:09:03', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (4, 'AgileBoot官网', 3, 'AgileBootguanwangIframeRouter', 0, '/AgileBootguanwangIframeLink', 0, '', '{"title":"AgileBoot官网","icon":"ep:link","showParent":true,"frameSrc":"https://element-plus.org/zh-CN/","rank":8}', 1, 'Agileboot官网地址', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:09:40', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (5, '用户管理', 1, 'SystemUser', 1, '/system/user/index', 0, 'system:user:list', '{"title":"用户管理","icon":"ep:user-filled","showParent":true}', 1, '用户管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:16:13', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (6, '角色管理', 1, 'SystemRole', 1, '/system/role/index', 0, 'system:role:list', '{"title":"角色管理","icon":"ep:user","showParent":true}', 1, '角色管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:16:23', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (7, '菜单管理', 1, 'MenuManagement', 1, '/system/menu/index', 0, 'system:menu:list', '{"title":"菜单管理","icon":"ep:menu","showParent":true}', 1, '菜单管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:41', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (8, '部门管理', 1, 'Department', 1, '/system/dept/index', 0, 'system:dept:list', '{"title":"部门管理","icon":"fa-solid:code-branch","showParent":true}', 1, '部门管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:35', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (9, '岗位管理', 1, 'Post', 1, '/system/post/index', 0, 'system:post:list', '{"title":"岗位管理","icon":"ep:postcard","showParent":true}', 1, '岗位管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:11', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (10, '参数设置', 1, 'Config', 1, '/system/config/index', 0, 'system:config:list', '{"title":"参数设置","icon":"ep:setting","showParent":true}', 1, '参数设置菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:03', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (11, '通知公告', 1, 'SystemNotice', 1, '/system/notice/index', 0, 'system:notice:list', '{"title":"通知公告","icon":"ep:notification","showParent":true}', 1, '通知公告菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:56', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (12, '日志管理', 1, 'LogManagement', 1, '/system/logd', 0, '', '{"title":"日志管理","icon":"ep:document","showParent":true}', 1, '日志管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:47', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (13, '在线用户', 1, 'OnlineUser', 2, '/system/monitor/onlineUser/index', 0, 'monitor:online:list', '{"title":"在线用户","icon":"fa-solid:users","showParent":true}', 1, '在线用户菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:13', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (14, '数据监控', 1, 'DataMonitor', 2, '/system/monitor/druid/index', 0, 'monitor:druid:list', '{"title":"数据监控","icon":"fa:database","showParent":true,"frameSrc":"/druid/login.html","isFrameSrcInternal":true}', 1, '数据监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:25', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (15, '服务监控', 1, 'ServerInfo', 2, '/system/monitor/server/index', 0, 'monitor:server:list', '{"title":"服务监控","icon":"fa:server","showParent":true}', 1, '服务监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:34', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (16, '缓存监控', 1, 'CacheInfo', 2, '/system/monitor/cache/index', 0, 'monitor:cache:list', '{"title":"缓存监控","icon":"ep:reading","showParent":true}', 1, '缓存监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:12:59', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (17, '系统接口', 1, 'SystemAPI', 3, '/tool/swagger/index', 0, 'tool:swagger:list', '{"title":"系统接口","icon":"ep:document-remove","showParent":true,"frameSrc":"/swagger-ui/index.html","isFrameSrcInternal":true}', 1, '系统接口菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:01', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (18, '操作日志', 1, 'OperationLog', 12, '/system/log/operationLog/index', 0, 'monitor:operlog:list', '{"title":"操作日志"}', 1, '操作日志菜单', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (19, '登录日志', 1, 'LoginLog', 12, '/system/log/loginLog/index', 0, 'monitor:logininfor:list', '{"title":"登录日志"}', 1, '登录日志菜单', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (20, '用户查询', 0, ' ', 5, '', 1, 'system:user:query', '{"title":"用户查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (21, '用户新增', 0, ' ', 5, '', 1, 'system:user:add', '{"title":"用户新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (22, '用户修改', 0, ' ', 5, '', 1, 'system:user:edit', '{"title":"用户修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (23, '用户删除', 0, ' ', 5, '', 1, 'system:user:remove', '{"title":"用户删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (24, '用户导出', 0, ' ', 5, '', 1, 'system:user:export', '{"title":"用户导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (25, '用户导入', 0, ' ', 5, '', 1, 'system:user:import', '{"title":"用户导入"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (26, '重置密码', 0, ' ', 5, '', 1, 'system:user:resetPwd', '{"title":"重置密码"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (27, '角色查询', 0, ' ', 6, '', 1, 'system:role:query', '{"title":"角色查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (28, '角色新增', 0, ' ', 6, '', 1, 'system:role:add', '{"title":"角色新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (29, '角色修改', 0, ' ', 6, '', 1, 'system:role:edit', '{"title":"角色修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (30, '角色删除', 0, ' ', 6, '', 1, 'system:role:remove', '{"title":"角色删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (31, '角色导出', 0, ' ', 6, '', 1, 'system:role:export', '{"title":"角色导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (32, '菜单查询', 0, ' ', 7, '', 1, 'system:menu:query', '{"title":"菜单查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (33, '菜单新增', 0, ' ', 7, '', 1, 'system:menu:add', '{"title":"菜单新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (34, '菜单修改', 0, ' ', 7, '', 1, 'system:menu:edit', '{"title":"菜单修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (35, '菜单删除', 0, ' ', 7, '', 1, 'system:menu:remove', '{"title":"菜单删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (36, '部门查询', 0, ' ', 8, '', 1, 'system:dept:query', '{"title":"部门查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (37, '部门新增', 0, ' ', 8, '', 1, 'system:dept:add', '{"title":"部门新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (38, '部门修改', 0, ' ', 8, '', 1, 'system:dept:edit', '{"title":"部门修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (39, '部门删除', 0, ' ', 8, '', 1, 'system:dept:remove', '{"title":"部门删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (40, '岗位查询', 0, ' ', 9, '', 1, 'system:post:query', '{"title":"岗位查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (41, '岗位新增', 0, ' ', 9, '', 1, 'system:post:add', '{"title":"岗位新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (42, '岗位修改', 0, ' ', 9, '', 1, 'system:post:edit', '{"title":"岗位修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (43, '岗位删除', 0, ' ', 9, '', 1, 'system:post:remove', '{"title":"岗位删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (44, '岗位导出', 0, ' ', 9, '', 1, 'system:post:export', '{"title":"岗位导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (45, '参数查询', 0, ' ', 10, '', 1, 'system:config:query', '{"title":"参数查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (46, '参数新增', 0, ' ', 10, '', 1, 'system:config:add', '{"title":"参数新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (47, '参数修改', 0, ' ', 10, '', 1, 'system:config:edit', '{"title":"参数修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (48, '参数删除', 0, ' ', 10, '', 1, 'system:config:remove', '{"title":"参数删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (49, '参数导出', 0, ' ', 10, '', 1, 'system:config:export', '{"title":"参数导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (50, '公告查询', 0, ' ', 11, '', 1, 'system:notice:query', '{"title":"公告查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (51, '公告新增', 0, ' ', 11, '', 1, 'system:notice:add', '{"title":"公告新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (52, '公告修改', 0, ' ', 11, '', 1, 'system:notice:edit', '{"title":"公告修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (53, '公告删除', 0, ' ', 11, '', 1, 'system:notice:remove', '{"title":"公告删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (54, '操作查询', 0, ' ', 18, '', 1, 'monitor:operlog:query', '{"title":"操作查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (55, '操作删除', 0, ' ', 18, '', 1, 'monitor:operlog:remove', '{"title":"操作删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (56, '日志导出', 0, ' ', 18, '', 1, 'monitor:operlog:export', '{"title":"日志导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (57, '登录查询', 0, ' ', 19, '', 1, 'monitor:logininfor:query', '{"title":"登录查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (58, '登录删除', 0, ' ', 19, '', 1, 'monitor:logininfor:remove', '{"title":"登录删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (59, '日志导出', 0, ' ', 19, '', 1, 'monitor:logininfor:export', '{"title":"日志导出","rank":22}', 1, '', 0, '2022-05-21 08:30:54', 1, '2023-07-22 17:02:28', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (60, '在线查询', 0, ' ', 13, '', 1, 'monitor:online:query', '{"title":"在线查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (61, '批量强退', 0, ' ', 13, '', 1, 'monitor:online:batchLogout', '{"title":"批量强退"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (62, '单条强退', 0, ' ', 13, '', 1, 'monitor:online:forceLogout', '{"title":"单条强退"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (63, 'AgileBoot Github地址', 4, 'https://github.com/valarchie/AgileBoot-Back-End', 0, '/external', 0, '', '{"title":"AgileBoot Github地址","icon":"fa-solid:external-link-alt","showParent":true,"rank":9}', 1, 'Agileboot github地址', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:12:13', 0);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (64, '首页', 2, '', 0, '/global', 0, '121212', '{"title":"首页","showParent":true,"rank":3}', 1, '', 1, '2023-07-24 22:36:03', 1, '2023-07-24 22:38:37', 1);
-INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (65, '个人中心', 1, 'PersonalCenter', 2053, '/system/user/profile', 0, '434sdf', '{"title":"个人中心","showParent":true,"rank":3}', 1, '', 1, '2023-07-24 22:36:55', null, null, 1);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (4, '用户管理', 1, 'SystemUser', 1, '/system/user/index', 0, 'system:user:list', '{"title":"用户管理","icon":"ep:user-filled","showParent":true}', 1, '用户管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:16:13', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (5, '角色管理', 1, 'SystemRole', 1, '/system/role/index', 0, 'system:role:list', '{"title":"角色管理","icon":"ep:user","showParent":true}', 1, '角色管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:16:23', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (6, '菜单管理', 1, 'MenuManagement', 1, '/system/menu/index', 0, 'system:menu:list', '{"title":"菜单管理","icon":"ep:menu","showParent":true}', 1, '菜单管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:41', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (7, '部门管理', 1, 'Department', 1, '/system/dept/index', 0, 'system:dept:list', '{"title":"部门管理","icon":"fa-solid:code-branch","showParent":true}', 1, '部门管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:35', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (8, '岗位管理', 1, 'Post', 1, '/system/post/index', 0, 'system:post:list', '{"title":"岗位管理","icon":"ep:postcard","showParent":true}', 1, '岗位管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:11', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (9, '参数设置', 1, 'Config', 1, '/system/config/index', 0, 'system:config:list', '{"title":"参数设置","icon":"ep:setting","showParent":true}', 1, '参数设置菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:15:03', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (10, '通知公告', 1, 'SystemNotice', 1, '/system/notice/index', 0, 'system:notice:list', '{"title":"通知公告","icon":"ep:notification","showParent":true}', 1, '通知公告菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:56', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (11, '日志管理', 1, 'LogManagement', 1, '/system/logd', 0, '', '{"title":"日志管理","icon":"ep:document","showParent":true}', 1, '日志管理菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:47', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (12, '在线用户', 1, 'OnlineUser', 2, '/system/monitor/onlineUser/index', 0, 'monitor:online:list', '{"title":"在线用户","icon":"fa-solid:users","showParent":true}', 1, '在线用户菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:13', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (13, '数据监控', 1, 'DataMonitor', 2, '/system/monitor/druid/index', 0, 'monitor:druid:list', '{"title":"数据监控","icon":"fa:database","showParent":true,"frameSrc":"/druid/login.html","isFrameSrcInternal":true}', 1, '数据监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:25', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (14, '服务监控', 1, 'ServerInfo', 2, '/system/monitor/server/index', 0, 'monitor:server:list', '{"title":"服务监控","icon":"fa:server","showParent":true}', 1, '服务监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:13:34', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (15, '缓存监控', 1, 'CacheInfo', 2, '/system/monitor/cache/index', 0, 'monitor:cache:list', '{"title":"缓存监控","icon":"ep:reading","showParent":true}', 1, '缓存监控菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:12:59', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (16, '系统接口', 1, 'SystemAPI', 3, '/tool/swagger/index', 0, 'tool:swagger:list', '{"title":"系统接口","icon":"ep:document-remove","showParent":true,"frameSrc":"/swagger-ui/index.html","isFrameSrcInternal":true}', 1, '系统接口菜单', 0, '2022-05-21 08:30:54', 1, '2023-08-14 23:14:01', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (17, '操作日志', 1, 'OperationLog', 12, '/system/log/operationLog/index', 0, 'monitor:operlog:list', '{"title":"操作日志"}', 1, '操作日志菜单', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (18, '登录日志', 1, 'LoginLog', 12, '/system/log/loginLog/index', 0, 'monitor:logininfor:list', '{"title":"登录日志"}', 1, '登录日志菜单', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (19, '用户查询', 0, ' ', 5, '', 1, 'system:user:query', '{"title":"用户查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (20, '用户新增', 0, ' ', 5, '', 1, 'system:user:add', '{"title":"用户新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (21, '用户修改', 0, ' ', 5, '', 1, 'system:user:edit', '{"title":"用户修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (22, '用户删除', 0, ' ', 5, '', 1, 'system:user:remove', '{"title":"用户删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (23, '用户导出', 0, ' ', 5, '', 1, 'system:user:export', '{"title":"用户导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (24, '用户导入', 0, ' ', 5, '', 1, 'system:user:import', '{"title":"用户导入"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (25, '重置密码', 0, ' ', 5, '', 1, 'system:user:resetPwd', '{"title":"重置密码"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (26, '角色查询', 0, ' ', 6, '', 1, 'system:role:query', '{"title":"角色查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (27, '角色新增', 0, ' ', 6, '', 1, 'system:role:add', '{"title":"角色新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (28, '角色修改', 0, ' ', 6, '', 1, 'system:role:edit', '{"title":"角色修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (29, '角色删除', 0, ' ', 6, '', 1, 'system:role:remove', '{"title":"角色删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (30, '角色导出', 0, ' ', 6, '', 1, 'system:role:export', '{"title":"角色导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (31, '菜单查询', 0, ' ', 7, '', 1, 'system:menu:query', '{"title":"菜单查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (32, '菜单新增', 0, ' ', 7, '', 1, 'system:menu:add', '{"title":"菜单新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (33, '菜单修改', 0, ' ', 7, '', 1, 'system:menu:edit', '{"title":"菜单修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (34, '菜单删除', 0, ' ', 7, '', 1, 'system:menu:remove', '{"title":"菜单删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (35, '部门查询', 0, ' ', 8, '', 1, 'system:dept:query', '{"title":"部门查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (36, '部门新增', 0, ' ', 8, '', 1, 'system:dept:add', '{"title":"部门新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (37, '部门修改', 0, ' ', 8, '', 1, 'system:dept:edit', '{"title":"部门修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (38, '部门删除', 0, ' ', 8, '', 1, 'system:dept:remove', '{"title":"部门删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (39, '岗位查询', 0, ' ', 9, '', 1, 'system:post:query', '{"title":"岗位查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (40, '岗位新增', 0, ' ', 9, '', 1, 'system:post:add', '{"title":"岗位新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (41, '岗位修改', 0, ' ', 9, '', 1, 'system:post:edit', '{"title":"岗位修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (42, '岗位删除', 0, ' ', 9, '', 1, 'system:post:remove', '{"title":"岗位删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (43, '岗位导出', 0, ' ', 9, '', 1, 'system:post:export', '{"title":"岗位导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (44, '参数查询', 0, ' ', 10, '', 1, 'system:config:query', '{"title":"参数查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (45, '参数新增', 0, ' ', 10, '', 1, 'system:config:add', '{"title":"参数新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (46, '参数修改', 0, ' ', 10, '', 1, 'system:config:edit', '{"title":"参数修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (47, '参数删除', 0, ' ', 10, '', 1, 'system:config:remove', '{"title":"参数删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (48, '参数导出', 0, ' ', 10, '', 1, 'system:config:export', '{"title":"参数导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (49, '公告查询', 0, ' ', 11, '', 1, 'system:notice:query', '{"title":"公告查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (50, '公告新增', 0, ' ', 11, '', 1, 'system:notice:add', '{"title":"公告新增"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (51, '公告修改', 0, ' ', 11, '', 1, 'system:notice:edit', '{"title":"公告修改"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (52, '公告删除', 0, ' ', 11, '', 1, 'system:notice:remove', '{"title":"公告删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (53, '操作查询', 0, ' ', 18, '', 1, 'monitor:operlog:query', '{"title":"操作查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (54, '操作删除', 0, ' ', 18, '', 1, 'monitor:operlog:remove', '{"title":"操作删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (55, '日志导出', 0, ' ', 18, '', 1, 'monitor:operlog:export', '{"title":"日志导出"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (56, '登录查询', 0, ' ', 19, '', 1, 'monitor:logininfor:query', '{"title":"登录查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (57, '登录删除', 0, ' ', 19, '', 1, 'monitor:logininfor:remove', '{"title":"登录删除"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (58, '日志导出', 0, ' ', 19, '', 1, 'monitor:logininfor:export', '{"title":"日志导出","rank":22}', 1, '', 0, '2022-05-21 08:30:54', 1, '2023-07-22 17:02:28', 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (59, '在线查询', 0, ' ', 13, '', 1, 'monitor:online:query', '{"title":"在线查询"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (60, '批量强退', 0, ' ', 13, '', 1, 'monitor:online:batchLogout', '{"title":"批量强退"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (61, '单条强退', 0, ' ', 13, '', 1, 'monitor:online:forceLogout', '{"title":"单条强退"}', 1, '', 0, '2022-05-21 08:30:54', null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (62, '首页', 2, '', 0, '/global', 0, '121212', '{"title":"首页","showParent":true,"rank":3}', 1, '', 1, '2023-07-24 22:36:03', 1, '2023-07-24 22:38:37', 1);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (63, '个人中心', 1, 'PersonalCenter', 2053, '/system/user/profile', 0, '434sdf', '{"title":"个人中心","showParent":true,"rank":3}', 1, '', 1, '2023-07-24 22:36:55', null, null, 1);
create table sys_notice
(
@@ -357,6 +360,123 @@ create table sys_user
comment '用户信息表';
INSERT INTO sys_user (user_id, post_id, role_id, dept_id, username, nickname, user_type, email, phone_number, sex, avatar, password, status, login_ip, login_date, is_admin, creator_id, create_time, updater_id, update_time, remark, deleted) VALUES (1, 1, 1, 4, 'admin', 'valarchie1', 0, 'agileboot@163.com', '15888888883', 0, '/profile/avatar/20230725164110_blob_6b7a989b1cdd4dd396665d2cfd2addc5.png', '$2a$10$o55UFZAtyWnDpRV6dvQe8.c/MjlFacC49ASj2usNXm9BY74SYI/uG', 1, '127.0.0.1', '2023-08-14 23:07:03', 1, null, '2022-05-21 08:30:54', 1, '2023-08-14 23:07:03', '管理员', 0);
-INSERT INTO sys_user (user_id, post_id, role_id, dept_id, username, nickname, user_type, email, phone_number, sex, avatar, password, status, login_ip, login_date, is_admin, creator_id, create_time, updater_id, update_time, remark, deleted) VALUES (2, 2, 2, 5, 'ag1', 'valarchie2', 0, 'agileboot1@qq.com', '15666666666', 1, '/profile/avatar/20230725114818_avatar_b5bf400732bb43369b4df58802049b22.png', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 1, '127.0.0.1', '2022-05-21 08:30:54', 0, null, '2022-05-21 08:30:54', null, null, '测试员1', 0);
-INSERT INTO sys_user (user_id, post_id, role_id, dept_id, username, nickname, user_type, email, phone_number, sex, avatar, password, status, login_ip, login_date, is_admin, creator_id, create_time, updater_id, update_time, remark, deleted) VALUES (3, 2, 0, 5, 'ag2', 'valarchie3', 0, 'agileboot2@qq.com', '15666666667', 1, '/profile/avatar/20230725114818_avatar_b5bf400732bb43369b4df58802049b22.png', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 1, '127.0.0.1', '2022-05-21 08:30:54', 0, null, '2022-05-21 08:30:54', null, null, '测试员2', 0);
+create table collaboration_record
+(
+ record_id bigint auto_increment comment '合作记录ID'
+ primary key,
+ brand varchar(100) not null comment '品牌',
+ goods varchar(100) not null comment '物品',
+ cooperation_platform varchar(50) null comment '合作平台',
+ image_return_num int default 1 not null comment '返图数量',
+ retained_method varchar(30) default '寄拍' not null comment '留存方式',
+ cooperated_method varchar(30) default '水下' not null comment '合作方式',
+ purchase_method varchar(30) default '拍单' not null comment '购入方式',
+ purchase_price decimal(10, 2) null comment '购入金额',
+ purchase_date date null comment '购入日期',
+ purchase_platform varchar(30) null comment '购入平台',
+ deadline date not null comment '预完成日期',
+ remuneration decimal(10, 2) null comment '稿费',
+ complete_date date null comment '完成日期',
+ requirements varchar(1000) null comment '拍摄要求',
+ remark varchar(1000) null comment '备注',
+ creator_id bigint null comment '创建者ID',
+ create_time datetime null comment '创建时间',
+ updater_id bigint null comment '更新者ID',
+ update_time datetime null comment '更新时间',
+ deleted tinyint(1) default 0 not null comment '逻辑删除',
+ index idx_collaboration_record_platform (cooperation_platform),
+ index idx_collaboration_record_deadline (deadline),
+ index idx_collaboration_record_purchase_date (purchase_date)
+)
+ comment '合作记录表';
+
+create table collaboration_task
+(
+ task_id bigint auto_increment comment '笔记任务ID'
+ primary key,
+ record_id bigint not null comment '合作记录ID',
+ release_date date null comment '发布日期',
+ sort_order int default 0 not null comment '排序',
+ creator_id bigint null comment '创建者ID',
+ create_time datetime null comment '创建时间',
+ updater_id bigint null comment '更新者ID',
+ update_time datetime null comment '更新时间',
+ deleted tinyint(1) default 0 not null comment '逻辑删除',
+ index idx_collaboration_task_record_id (record_id)
+)
+ comment '合作笔记任务表';
+
+create table collaboration_expenditure
+(
+ expenditure_id bigint auto_increment comment '支出ID'
+ primary key,
+ record_id bigint not null comment '合作记录ID',
+ spend_date date null comment '支出日期',
+ amount decimal(10, 2) null comment '支出金额',
+ purpose varchar(30) null comment '支出用途',
+ creator_id bigint null comment '创建者ID',
+ create_time datetime null comment '创建时间',
+ updater_id bigint null comment '更新者ID',
+ update_time datetime null comment '更新时间',
+ deleted tinyint(1) default 0 not null comment '逻辑删除',
+ index idx_collaboration_expenditure_record_id (record_id),
+ index idx_collaboration_expenditure_spend_date (spend_date)
+)
+ comment '合作支出表';
+
+create table collaboration_settlement
+(
+ settlement_id bigint auto_increment comment '结款ID'
+ primary key,
+ record_id bigint not null comment '合作记录ID',
+ settle_date date null comment '结款日期',
+ method varchar(30) null comment '结款方式',
+ income decimal(10, 2) null comment '结款金额',
+ purpose varchar(30) null comment '结款用途',
+ creator_id bigint null comment '创建者ID',
+ create_time datetime null comment '创建时间',
+ updater_id bigint null comment '更新者ID',
+ update_time datetime null comment '更新时间',
+ deleted tinyint(1) default 0 not null comment '逻辑删除',
+ index idx_collaboration_settlement_record_id (record_id),
+ index idx_collaboration_settlement_date (settle_date),
+ index idx_collaboration_settlement_purpose (purpose)
+)
+ comment '合作结款表';
+
+create table collaboration_file
+(
+ file_id bigint auto_increment comment '文件ID'
+ primary key,
+ record_id bigint not null comment '合作记录ID',
+ file_type varchar(30) not null comment '文件类型:GOODS_IMAGE/ATTACHMENT',
+ url varchar(500) not null comment '访问地址',
+ file_name varchar(300) null comment '服务端相对路径',
+ new_file_name varchar(200) null comment '服务端新文件名',
+ original_filename varchar(300) null comment '原始文件名',
+ sort_order int default 0 not null comment '排序',
+ creator_id bigint null comment '创建者ID',
+ create_time datetime null comment '创建时间',
+ updater_id bigint null comment '更新者ID',
+ update_time datetime null comment '更新时间',
+ deleted tinyint(1) default 0 not null comment '逻辑删除',
+ index idx_collaboration_file_record_id (record_id),
+ index idx_collaboration_file_type (file_type)
+)
+ comment '合作文件表';
+
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3000, '合作管理', 2, '', 0, '/collaboration', 0, '', '{"title":"合作管理","icon":"ep:connection","showParent":true,"rank":4}', 1, '合作管理目录', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3001, '合作记录', 1, 'CollaborationRecord', 3000, '/collaboration/record/index', 0, 'collaboration:record:list', '{"title":"合作记录","icon":"ep:notebook","showParent":true}', 1, '合作记录菜单', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3002, '月度统计', 1, 'CollaborationStatistics', 3000, '/collaboration/statistics/index', 0, 'collaboration:record:statistics', '{"title":"月度统计","icon":"ep:data-analysis","showParent":true}', 1, '月度统计菜单', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3003, '合作查询', 0, ' ', 3001, '', 1, 'collaboration:record:query', '{"title":"合作查询"}', 1, '', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3004, '合作新增', 0, ' ', 3001, '', 1, 'collaboration:record:add', '{"title":"合作新增"}', 1, '', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3005, '合作修改', 0, ' ', 3001, '', 1, 'collaboration:record:edit', '{"title":"合作修改"}', 1, '', 0, now(), null, null, 0);
+INSERT INTO sys_menu (menu_id, menu_name, menu_type, router_name, parent_id, path, is_button, permission, meta_info, status, remark, creator_id, create_time, updater_id, update_time, deleted) VALUES (3006, '合作删除', 0, ' ', 3001, '', 1, 'collaboration:record:remove', '{"title":"合作删除"}', 1, '', 0, now(), null, null, 0);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3000);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3001);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3002);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3003);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3004);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3005);
+INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 3006);
diff --git a/backend/sql/agileboot_20221007.sql b/backend/sql/agileboot_20221007.sql
deleted file mode 100644
index 853efb2..0000000
--- a/backend/sql/agileboot_20221007.sql
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
-Navicat MySQL Data Transfer
-
-Source Server : Local-Mysql
-Source Server Version : 80029
-Source Host : localhost:33066
-Source Database : agileboot
-
-Target Server Type : MYSQL
-Target Server Version : 80029
-File Encoding : 65001
-
-Date: 2022-10-07 16:05:40
-*/
-
-SET FOREIGN_KEY_CHECKS=0;
-
--- ----------------------------
--- Table structure for sys_config
--- ----------------------------
-DROP TABLE IF EXISTS `sys_config`;
-CREATE TABLE `sys_config` (
- `config_id` int NOT NULL AUTO_INCREMENT COMMENT '参数主键',
- `config_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置名称',
- `config_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置键名',
- `config_options` varchar(1024) NOT NULL DEFAULT '' COMMENT '可选的选项',
- `config_value` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置值',
- `is_allow_change` tinyint(1) NOT NULL COMMENT '是否允许修改',
- `creator_id` bigint DEFAULT NULL COMMENT '创建者ID',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `remark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`config_id`),
- UNIQUE KEY `config_key_uniq_idx` (`config_key`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='参数配置表';
-
--- ----------------------------
--- Records of sys_config
--- ----------------------------
-INSERT INTO `sys_config` VALUES ('1', '主框架页-默认皮肤样式名称', 'sys.index.skinName', '["skin-blue","skin-green","skin-purple","skin-red","skin-yellow"]', 'skin-blue', '1', null, null, '2022-08-28 22:12:19', '2022-05-21 08:30:55', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow', '0');
-INSERT INTO `sys_config` VALUES ('2', '用户管理-账号初始密码', 'sys.user.initPassword', '', '1234567', '1', null, null, '2022-08-28 21:54:19', '2022-05-21 08:30:55', '初始化密码 123456', '0');
-INSERT INTO `sys_config` VALUES ('3', '主框架页-侧边栏主题', 'sys.index.sideTheme', '["theme-dark","theme-light"]', 'theme-dark', '1', null, null, '2022-08-28 22:12:15', '2022-08-20 08:30:55', '深色主题theme-dark,浅色主题theme-light', '0');
-INSERT INTO `sys_config` VALUES ('4', '账号自助-验证码开关', 'sys.account.captchaOnOff', '["true","false"]', 'false', '0', null, null, '2022-08-28 22:03:37', '2022-05-21 08:30:55', '是否开启验证码功能(true开启,false关闭)', '0');
-INSERT INTO `sys_config` VALUES ('5', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', '["true","false"]', 'true', '0', null, '1', '2022-10-05 22:18:57', '2022-05-21 08:30:55', '是否开启注册用户功能(true开启,false关闭)', '0');
-
-
--- ----------------------------
--- Table structure for sys_dept
--- ----------------------------
-DROP TABLE IF EXISTS `sys_dept`;
-CREATE TABLE `sys_dept` (
- `dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',
- `parent_id` bigint NOT NULL DEFAULT '0' COMMENT '父部门id',
- `ancestors` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '祖级列表',
- `dept_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门名称',
- `order_num` int NOT NULL DEFAULT '0' COMMENT '显示顺序',
- `leader_id` bigint DEFAULT NULL,
- `leader_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '负责人',
- `phone` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '联系电话',
- `email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '邮箱',
- `status` smallint NOT NULL DEFAULT '0' COMMENT '部门状态(0停用 1启用)',
- `creator_id` bigint DEFAULT NULL COMMENT '创建者ID',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`dept_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=203 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门表';
-
--- ----------------------------
--- Records of sys_dept
--- ----------------------------
-INSERT INTO `sys_dept` VALUES ('1', '0', '0', 'AgileBoot科技', '0', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('2', '1', '0,1', '深圳总公司', '1', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('3', '1', '0,1', '长沙分公司', '2', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('4', '2', '0,1,2', '研发部门', '1', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('5', '2', '0,1,2', '市场部门', '2', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('6', '2', '0,1,2', '测试部门', '3', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('7', '2', '0,1,2', '财务部门', '4', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('8', '2', '0,1,2', '运维部门', '5', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('9', '3', '0,1,3', '市场部门', '1', null, 'valarchie', '15888888888', 'valarchie@163.com', '1', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_dept` VALUES ('10', '3', '0,1,3', '财务部门', '2', null, 'valarchie', '15888888888', 'valarchie@163.com', '0', null, '2022-05-21 08:30:54', null, null, '0');
-
--- ----------------------------
--- Table structure for sys_login_info
--- ----------------------------
-DROP TABLE IF EXISTS `sys_login_info`;
-CREATE TABLE `sys_login_info` (
- `info_id` bigint NOT NULL AUTO_INCREMENT COMMENT '访问ID',
- `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户账号',
- `ip_address` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '登录IP地址',
- `login_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '登录地点',
- `browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '浏览器类型',
- `operation_system` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '操作系统',
- `status` smallint NOT NULL DEFAULT '0' COMMENT '登录状态(1成功 0失败)',
- `msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '提示消息',
- `login_time` datetime DEFAULT NULL COMMENT '访问时间',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`info_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=415 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统访问记录';
-
--- ----------------------------
--- Records of sys_login_info
--- ----------------------------
-
--- ----------------------------
--- Table structure for sys_menu
--- ----------------------------
-DROP TABLE IF EXISTS `sys_menu`;
-CREATE TABLE `sys_menu` (
- `menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
- `menu_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称',
- `parent_id` bigint NOT NULL DEFAULT '0' COMMENT '父菜单ID',
- `order_num` int NOT NULL DEFAULT '0' COMMENT '显示顺序',
- `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '路由地址',
- `component` varchar(255) DEFAULT NULL COMMENT '组件路径',
- `query` varchar(255) DEFAULT NULL COMMENT '路由参数',
- `is_external` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否为外链(1是 0否)',
- `is_cache` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否缓存(1缓存 0不缓存)',
- `menu_type` smallint NOT NULL DEFAULT '0' COMMENT '菜单类型(M=1目录 C=2菜单 F=3按钮)',
- `is_visible` tinyint(1) NOT NULL DEFAULT '0' COMMENT '菜单状态(1显示 0隐藏)',
- `status` smallint NOT NULL DEFAULT '0' COMMENT '菜单状态(1启用 0停用)',
- `perms` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '权限标识',
- `icon` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '#' COMMENT '菜单图标',
- `creator_id` bigint DEFAULT NULL COMMENT '创建者ID',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`menu_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=2007 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='菜单权限表';
-
--- ----------------------------
--- Records of sys_menu
--- ----------------------------
-INSERT INTO `sys_menu` VALUES ('1', '系统管理', '0', '1', 'system', null, '', '0', '1', '1', '1', '1', '', 'system', '0', '2022-05-21 08:30:54', null, null, '系统管理目录', '0');
-INSERT INTO `sys_menu` VALUES ('2', '系统监控', '0', '2', 'monitor', null, '', '0', '1', '1', '1', '1', '', 'monitor', '0', '2022-05-21 08:30:54', null, null, '系统监控目录', '0');
-INSERT INTO `sys_menu` VALUES ('3', '系统工具', '0', '3', 'tool', null, '', '0', '1', '1', '1', '1', '', 'tool', '0', '2022-05-21 08:30:54', null, null, '系统工具目录', '0');
-INSERT INTO `sys_menu` VALUES ('4', 'AgileBoot官网', '0', '4', 'https://juejin.cn/column/7159946528827080734', null, '', '1', '1', '1', '1', '1', '', 'guide', '0', '2022-05-21 08:30:54', null, null, 'Agileboot官网地址', '0');
-INSERT INTO `sys_menu` VALUES ('5', '用户管理', '1', '1', 'user', 'system/user/index', '', '0', '1', '2', '1', '1', 'system:user:list', 'user', '0', '2022-05-21 08:30:54', null, null, '用户管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('6', '角色管理', '1', '2', 'role', 'system/role/index', '', '0', '1', '2', '1', '1', 'system:role:list', 'peoples', '0', '2022-05-21 08:30:54', null, null, '角色管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('7', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '0', '1', '2', '1', '1', 'system:menu:list', 'tree-table', '0', '2022-05-21 08:30:54', null, null, '菜单管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('8', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '0', '1', '2', '1', '1', 'system:dept:list', 'tree', '0', '2022-05-21 08:30:54', null, null, '部门管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('9', '岗位管理', '1', '5', 'post', 'system/post/index', '', '0', '1', '2', '1', '1', 'system:post:list', 'post', '0', '2022-05-21 08:30:54', null, null, '岗位管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('10', '参数设置', '1', '7', 'config', 'system/config/index', '', '0', '1', '2', '1', '1', 'system:config:list', 'edit', '0', '2022-05-21 08:30:54', null, null, '参数设置菜单', '0');
-INSERT INTO `sys_menu` VALUES ('11', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '0', '1', '2', '1', '1', 'system:notice:list', 'message', '0', '2022-05-21 08:30:54', null, null, '通知公告菜单', '0');
-INSERT INTO `sys_menu` VALUES ('12', '日志管理', '1', '9', 'log', '', '', '0', '1', '1', '1', '1', '', 'log', '0', '2022-05-21 08:30:54', null, null, '日志管理菜单', '0');
-INSERT INTO `sys_menu` VALUES ('13', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '0', '1', '2', '1', '1', 'monitor:online:list', 'online', '0', '2022-05-21 08:30:54', null, null, '在线用户菜单', '0');
-INSERT INTO `sys_menu` VALUES ('14', '数据监控', '2', '3', 'druid', 'monitor/druid/index', '', '0', '1', '2', '1', '1', 'monitor:druid:list', 'druid', '0', '2022-05-21 08:30:54', null, null, '数据监控菜单', '0');
-INSERT INTO `sys_menu` VALUES ('15', '服务监控', '2', '4', 'server', 'monitor/server/index', '', '0', '1', '2', '1', '1', 'monitor:server:list', 'server', '0', '2022-05-21 08:30:54', null, null, '服务监控菜单', '0');
-INSERT INTO `sys_menu` VALUES ('16', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '0', '1', '2', '1', '1', 'monitor:cache:list', 'redis', '0', '2022-05-21 08:30:54', null, null, '缓存监控菜单', '0');
-INSERT INTO `sys_menu` VALUES ('17', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', '', '0', '1', '2', '1', '1', 'tool:swagger:list', 'swagger', '0', '2022-05-21 08:30:54', null, null, '系统接口菜单', '0');
-INSERT INTO `sys_menu` VALUES ('18', '操作日志', '12', '1', 'operlog', 'monitor/operlog/index', '', '0', '1', '2', '1', '1', 'monitor:operlog:list', 'form', '0', '2022-05-21 08:30:54', null, null, '操作日志菜单', '0');
-INSERT INTO `sys_menu` VALUES ('19', '登录日志', '12', '2', 'logininfor', 'monitor/logininfor/index', '', '0', '1', '2', '1', '1', 'monitor:logininfor:list', 'logininfor', '0', '2022-05-21 08:30:54', null, null, '登录日志菜单', '0');
-INSERT INTO `sys_menu` VALUES ('20', '用户查询', '5', '1', '', '', '', '0', '1', '3', '1', '1', 'system:user:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('21', '用户新增', '5', '2', '', '', '', '0', '1', '3', '1', '1', 'system:user:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('22', '用户修改', '5', '3', '', '', '', '0', '1', '3', '1', '1', 'system:user:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('23', '用户删除', '5', '4', '', '', '', '0', '1', '3', '1', '1', 'system:user:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('24', '用户导出', '5', '5', '', '', '', '0', '1', '3', '1', '1', 'system:user:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('25', '用户导入', '5', '6', '', '', '', '0', '1', '3', '1', '1', 'system:user:import', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('26', '重置密码', '5', '7', '', '', '', '0', '1', '3', '1', '1', 'system:user:resetPwd', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('27', '角色查询', '6', '1', '', '', '', '0', '1', '3', '1', '1', 'system:role:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('28', '角色新增', '6', '2', '', '', '', '0', '1', '3', '1', '1', 'system:role:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('29', '角色修改', '6', '3', '', '', '', '0', '1', '3', '1', '1', 'system:role:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('30', '角色删除', '6', '4', '', '', '', '0', '1', '3', '1', '1', 'system:role:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('31', '角色导出', '6', '5', '', '', '', '0', '1', '3', '1', '1', 'system:role:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('32', '菜单查询', '7', '1', '', '', '', '0', '1', '3', '1', '1', 'system:menu:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('33', '菜单新增', '7', '2', '', '', '', '0', '1', '3', '1', '1', 'system:menu:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('34', '菜单修改', '7', '3', '', '', '', '0', '1', '3', '1', '1', 'system:menu:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('35', '菜单删除', '7', '4', '', '', '', '0', '1', '3', '1', '1', 'system:menu:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('36', '部门查询', '8', '1', '', '', '', '0', '1', '3', '1', '1', 'system:dept:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('37', '部门新增', '8', '2', '', '', '', '0', '1', '3', '1', '1', 'system:dept:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('38', '部门修改', '8', '3', '', '', '', '0', '1', '3', '1', '1', 'system:dept:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('39', '部门删除', '8', '4', '', '', '', '0', '1', '3', '1', '1', 'system:dept:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('40', '岗位查询', '9', '1', '', '', '', '0', '1', '3', '1', '1', 'system:post:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('41', '岗位新增', '9', '2', '', '', '', '0', '1', '3', '1', '1', 'system:post:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('42', '岗位修改', '9', '3', '', '', '', '0', '1', '3', '1', '1', 'system:post:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('43', '岗位删除', '9', '4', '', '', '', '0', '1', '3', '1', '1', 'system:post:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('44', '岗位导出', '9', '5', '', '', '', '0', '1', '3', '1', '1', 'system:post:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('45', '参数查询', '10', '1', '#', '', '', '0', '1', '3', '1', '1', 'system:config:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('46', '参数新增', '10', '2', '#', '', '', '0', '1', '3', '1', '1', 'system:config:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('47', '参数修改', '10', '3', '#', '', '', '0', '1', '3', '1', '1', 'system:config:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('48', '参数删除', '10', '4', '#', '', '', '0', '1', '3', '1', '1', 'system:config:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('49', '参数导出', '10', '5', '#', '', '', '0', '1', '3', '1', '1', 'system:config:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('50', '公告查询', '11', '1', '#', '', '', '0', '1', '3', '1', '1', 'system:notice:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('51', '公告新增', '11', '2', '#', '', '', '0', '1', '3', '1', '1', 'system:notice:add', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('52', '公告修改', '11', '3', '#', '', '', '0', '1', '3', '1', '1', 'system:notice:edit', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('53', '公告删除', '11', '4', '#', '', '', '0', '1', '3', '1', '1', 'system:notice:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('54', '操作查询', '18', '1', '#', '', '', '0', '1', '3', '1', '1', 'monitor:operlog:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('55', '操作删除', '18', '2', '#', '', '', '0', '1', '3', '1', '1', 'monitor:operlog:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('56', '日志导出', '18', '4', '#', '', '', '0', '1', '3', '1', '1', 'monitor:operlog:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('57', '登录查询', '19', '1', '#', '', '', '0', '1', '3', '1', '1', 'monitor:logininfor:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('58', '登录删除', '19', '2', '#', '', '', '0', '1', '3', '1', '1', 'monitor:logininfor:remove', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('59', '日志导出', '19', '3', '#', '', '', '0', '1', '3', '1', '1', 'monitor:logininfor:export', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('60', '在线查询', '13', '1', '#', '', '', '0', '1', '3', '1', '1', 'monitor:online:query', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('61', '批量强退', '13', '2', '#', '', '', '0', '1', '3', '1', '1', 'monitor:online:batchLogout', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-INSERT INTO `sys_menu` VALUES ('62', '单条强退', '13', '3', '#', '', '', '0', '1', '3', '1', '1', 'monitor:online:forceLogout', '#', '0', '2022-05-21 08:30:54', null, null, '', '0');
-
-
--- ----------------------------
--- Table structure for sys_notice
--- ----------------------------
-DROP TABLE IF EXISTS `sys_notice`;
-CREATE TABLE `sys_notice` (
- `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID',
- `notice_title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告标题',
- `notice_type` smallint NOT NULL COMMENT '公告类型(1通知 2公告)',
- `notice_content` text COMMENT '公告内容',
- `status` smallint NOT NULL DEFAULT '0' COMMENT '公告状态(1正常 0关闭)',
- `creator_id` bigint NOT NULL COMMENT '创建者ID',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`notice_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='通知公告表';
-
--- ----------------------------
--- Records of sys_notice
--- ----------------------------
-INSERT INTO `sys_notice` VALUES ('1', '温馨提醒:2018-07-01 AgileBoot新版本发布啦', '2', '新版本内容~~~~~~~~~~', '1', '1', '2022-05-21 08:30:55', '1', '2022-08-29 20:12:37', '管理员', '0');
-INSERT INTO `sys_notice` VALUES ('2', '维护通知:2018-07-01 AgileBoot系统凌晨维护', '1', '维护内容', '1', '1', '2022-05-21 08:30:55', null, null, '管理员', '0');
-
-
--- ----------------------------
--- Table structure for sys_operation_log
--- ----------------------------
-DROP TABLE IF EXISTS `sys_operation_log`;
-CREATE TABLE `sys_operation_log` (
- `operation_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
- `business_type` smallint NOT NULL DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
- `request_method` smallint NOT NULL DEFAULT '0' COMMENT '请求方式',
- `request_module` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '请求模块',
- `request_url` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '请求URL',
- `called_method` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '调用方法',
- `operator_type` smallint NOT NULL DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
- `user_id` bigint DEFAULT '0' COMMENT '用户ID',
- `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作人员',
- `operator_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作人员ip',
- `operator_location` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作地点',
- `dept_id` bigint DEFAULT '0' COMMENT '部门ID',
- `dept_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '部门名称',
- `operation_param` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '请求参数',
- `operation_result` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '返回参数',
- `status` smallint NOT NULL DEFAULT '1' COMMENT '操作状态(1正常 0异常)',
- `error_stack` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '错误消息',
- `operation_time` datetime NOT NULL COMMENT '操作时间',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`operation_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=379 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='操作日志记录';
-
--- ----------------------------
--- Records of sys_operation_log
--- ----------------------------
-
--- ----------------------------
--- Table structure for sys_post
--- ----------------------------
-DROP TABLE IF EXISTS `sys_post`;
-CREATE TABLE `sys_post` (
- `post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID',
- `post_code` varchar(64) NOT NULL COMMENT '岗位编码',
- `post_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位名称',
- `post_sort` int NOT NULL COMMENT '显示顺序',
- `status` smallint NOT NULL COMMENT '状态(1正常 0停用)',
- `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
- `creator_id` bigint DEFAULT NULL,
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL,
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
- PRIMARY KEY (`post_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='岗位信息表';
-
--- ----------------------------
--- Records of sys_post
--- ----------------------------
-INSERT INTO `sys_post` VALUES ('1', 'ceo', '董事长', '1', '1', '', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_post` VALUES ('2', 'se', '项目经理', '2', '1', '', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_post` VALUES ('3', 'hr', '人力资源', '3', '1', '', null, '2022-05-21 08:30:54', null, null, '0');
-INSERT INTO `sys_post` VALUES ('4', 'user', '普通员工', '5', '0', '', null, '2022-05-21 08:30:54', null, null, '0');
-
--- ----------------------------
--- Table structure for sys_role
--- ----------------------------
-DROP TABLE IF EXISTS `sys_role`;
-CREATE TABLE `sys_role` (
- `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',
- `role_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称',
- `role_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色权限字符串',
- `role_sort` int NOT NULL COMMENT '显示顺序',
- `data_scope` smallint DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3: 本部门数据权限 4: 本部门及以下数据权限 5: 本人权限)',
- `dept_id_set` varchar(1024) DEFAULT '' COMMENT '角色所拥有的部门数据权限',
- `status` smallint NOT NULL COMMENT '角色状态(1正常 0停用)',
- `creator_id` bigint DEFAULT NULL COMMENT '创建者ID',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
- PRIMARY KEY (`role_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色信息表';
-
--- ----------------------------
--- Records of sys_role
--- ----------------------------
-INSERT INTO `sys_role` VALUES ('1', '超级管理员', 'admin', '1', '1', '', '1', null, '2022-05-21 08:30:54', null, null, '超级管理员', '0');
-INSERT INTO `sys_role` VALUES ('2', '普通角色', 'common', '3', '2', '', '1', null, '2022-05-21 08:30:54', null, null, '普通角色', '0');
-INSERT INTO `sys_role` VALUES ('3', '闲置角色', 'unused', '4', '2', '', '0', null, '2022-05-21 08:30:54', null, null, '未使用的角色', '0');
-
--- ----------------------------
--- Table structure for sys_role_menu
--- ----------------------------
-DROP TABLE IF EXISTS `sys_role_menu`;
-CREATE TABLE `sys_role_menu` (
- `role_id` bigint NOT NULL COMMENT '角色ID',
- `menu_id` bigint NOT NULL COMMENT '菜单ID',
- PRIMARY KEY (`role_id`,`menu_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色和菜单关联表';
-
--- ----------------------------
--- Records of sys_role_menu
--- ----------------------------
-INSERT INTO `sys_role_menu` VALUES ('2', '1');
-INSERT INTO `sys_role_menu` VALUES ('2', '2');
-INSERT INTO `sys_role_menu` VALUES ('2', '3');
-INSERT INTO `sys_role_menu` VALUES ('2', '4');
-INSERT INTO `sys_role_menu` VALUES ('2', '5');
-INSERT INTO `sys_role_menu` VALUES ('2', '6');
-INSERT INTO `sys_role_menu` VALUES ('2', '7');
-INSERT INTO `sys_role_menu` VALUES ('2', '8');
-INSERT INTO `sys_role_menu` VALUES ('2', '9');
-INSERT INTO `sys_role_menu` VALUES ('2', '10');
-INSERT INTO `sys_role_menu` VALUES ('2', '11');
-INSERT INTO `sys_role_menu` VALUES ('2', '12');
-INSERT INTO `sys_role_menu` VALUES ('2', '13');
-INSERT INTO `sys_role_menu` VALUES ('2', '14');
-INSERT INTO `sys_role_menu` VALUES ('2', '15');
-INSERT INTO `sys_role_menu` VALUES ('2', '16');
-INSERT INTO `sys_role_menu` VALUES ('2', '17');
-INSERT INTO `sys_role_menu` VALUES ('2', '18');
-INSERT INTO `sys_role_menu` VALUES ('2', '19');
-INSERT INTO `sys_role_menu` VALUES ('2', '20');
-INSERT INTO `sys_role_menu` VALUES ('2', '21');
-INSERT INTO `sys_role_menu` VALUES ('2', '22');
-INSERT INTO `sys_role_menu` VALUES ('2', '23');
-INSERT INTO `sys_role_menu` VALUES ('2', '24');
-INSERT INTO `sys_role_menu` VALUES ('2', '25');
-INSERT INTO `sys_role_menu` VALUES ('2', '26');
-INSERT INTO `sys_role_menu` VALUES ('2', '27');
-INSERT INTO `sys_role_menu` VALUES ('2', '28');
-INSERT INTO `sys_role_menu` VALUES ('2', '29');
-INSERT INTO `sys_role_menu` VALUES ('2', '30');
-INSERT INTO `sys_role_menu` VALUES ('2', '31');
-INSERT INTO `sys_role_menu` VALUES ('2', '32');
-INSERT INTO `sys_role_menu` VALUES ('2', '33');
-INSERT INTO `sys_role_menu` VALUES ('2', '34');
-INSERT INTO `sys_role_menu` VALUES ('2', '35');
-INSERT INTO `sys_role_menu` VALUES ('2', '36');
-INSERT INTO `sys_role_menu` VALUES ('2', '37');
-INSERT INTO `sys_role_menu` VALUES ('2', '38');
-INSERT INTO `sys_role_menu` VALUES ('2', '39');
-INSERT INTO `sys_role_menu` VALUES ('2', '40');
-INSERT INTO `sys_role_menu` VALUES ('2', '41');
-INSERT INTO `sys_role_menu` VALUES ('2', '42');
-INSERT INTO `sys_role_menu` VALUES ('2', '43');
-INSERT INTO `sys_role_menu` VALUES ('2', '44');
-INSERT INTO `sys_role_menu` VALUES ('2', '45');
-INSERT INTO `sys_role_menu` VALUES ('2', '46');
-INSERT INTO `sys_role_menu` VALUES ('2', '47');
-INSERT INTO `sys_role_menu` VALUES ('2', '48');
-INSERT INTO `sys_role_menu` VALUES ('2', '49');
-INSERT INTO `sys_role_menu` VALUES ('2', '50');
-INSERT INTO `sys_role_menu` VALUES ('2', '51');
-INSERT INTO `sys_role_menu` VALUES ('2', '52');
-INSERT INTO `sys_role_menu` VALUES ('2', '53');
-INSERT INTO `sys_role_menu` VALUES ('2', '54');
-INSERT INTO `sys_role_menu` VALUES ('2', '55');
-INSERT INTO `sys_role_menu` VALUES ('2', '56');
-INSERT INTO `sys_role_menu` VALUES ('2', '57');
-INSERT INTO `sys_role_menu` VALUES ('2', '58');
-INSERT INTO `sys_role_menu` VALUES ('2', '59');
-INSERT INTO `sys_role_menu` VALUES ('2', '60');
-INSERT INTO `sys_role_menu` VALUES ('2', '61');
--- roleId = 2的权限 特地少一个 方便测试
-INSERT INTO `sys_role_menu` VALUES ('3', '1');
-
-
--- ----------------------------
--- Table structure for sys_user
--- ----------------------------
-DROP TABLE IF EXISTS `sys_user`;
-CREATE TABLE `sys_user` (
- `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
- `post_id` bigint DEFAULT NULL COMMENT '职位id',
- `role_id` bigint DEFAULT NULL COMMENT '角色id',
- `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
- `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户账号',
- `nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户昵称',
- `user_type` smallint DEFAULT '0' COMMENT '用户类型(00系统用户)',
- `email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户邮箱',
- `phone_number` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '手机号码',
- `sex` smallint DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
- `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '头像地址',
- `password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码',
- `status` smallint NOT NULL DEFAULT '0' COMMENT '帐号状态(1正常 2停用 3冻结)',
- `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '最后登录IP',
- `login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
- `is_admin` tinyint(1) NOT NULL DEFAULT '0' COMMENT '超级管理员标志(1是,0否)',
- `creator_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `create_time` datetime DEFAULT NULL COMMENT '创建时间',
- `updater_id` bigint DEFAULT NULL COMMENT '更新者ID',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
- `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
- PRIMARY KEY (`user_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户信息表';
-
--- ----------------------------
--- Records of sys_user
--- ----------------------------
-INSERT INTO `sys_user` VALUES ('1', '1', '1', '4', 'admin', 'valarchie1', '0', 'agileboot@163.com', '15888888889', '0', '', '$2a$10$rb1wRoEIkLbIknREEN1LH.FGs4g0oOS5t6l5LQ793nRaFO.SPHDHy', '1', '127.0.0.1', '2022-10-06 17:00:06', 1, null, '2022-05-21 08:30:54', '1', '2022-10-06 17:00:06', '管理员', '0');
-INSERT INTO `sys_user` VALUES ('2', '2', '2', '5', 'ag1', 'valarchie2', '0', 'agileboot1@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '1', '127.0.0.1', '2022-05-21 08:30:54', 0, null, '2022-05-21 08:30:54', null, null, '测试员1', '0');
-INSERT INTO `sys_user` VALUES ('3', '2', '0', '5', 'ag2', 'valarchie3', '0', 'agileboot2@qq.com', '15666666667', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '1', '127.0.0.1', '2022-05-21 08:30:54', 0, null, '2022-05-21 08:30:54', null, null, '测试员2', '0');
diff --git a/docs/backend-feature-development.md b/docs/backend-feature-development.md
new file mode 100644
index 0000000..77f18d1
--- /dev/null
+++ b/docs/backend-feature-development.md
@@ -0,0 +1,155 @@
+# 后端业务功能开发规范
+
+本文是本项目后端新增或修改业务功能时的权威规范。开发后台管理端业务模块时,必须优先遵循本项目现有架构,而不是套用通用 Spring Boot 三层模板。
+
+## 架构原则
+
+后端业务代码按 `Controller -> ApplicationService -> Model -> db Service/Mapper` 组织。
+
+优先参考这些现有模块:
+
+- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/post`
+- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/user`
+- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/role`
+- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/notice`
+- `backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/system`
+
+不要让 Controller 直接调用 Mapper,不要把业务规则写在 Controller,也不要直接把 Entity 或 DO 返回给前端。
+
+## 开发准备
+
+开发新后端业务功能前,应先确认需求所属领域、涉及的数据表、接口范围、权限标识和相似模块。本文是后端业务功能开发的统一规范。
+
+开始编码前必须先做一次相似模块调研,确认目标功能最接近 `post`、`user`、`role`、`notice` 还是其他模块,再按相似模块的命名、包路径、注解、分页、异常、权限、日志和测试风格实现。
+
+## 目录结构
+
+后台管理端入口放在 `agileboot-admin`:
+
+```text
+backend/agileboot-admin/src/main/java/com/agileboot/admin/controller//
+└── XxxController.java
+```
+
+业务代码放在 `agileboot-domain`:
+
+```text
+backend/agileboot-domain/src/main/java/com/agileboot/domain//xxx/
+├── XxxApplicationService.java
+├── command/
+│ ├── AddXxxCommand.java
+│ └── UpdateXxxCommand.java
+├── query/
+│ └── XxxQuery.java
+├── dto/
+│ ├── XxxDTO.java
+│ └── XxxDetailDTO.java
+├── model/
+│ ├── XxxModel.java
+│ └── XxxModelFactory.java
+└── db/
+ ├── XxxEntity.java
+ ├── XxxDO.java
+ ├── XxxMapper.java
+ ├── XxxService.java
+ └── XxxServiceImpl.java
+```
+
+`XxxDO` 是可选文件。只有当查询结果包含 join、聚合、报表字段或其他不完全匹配 Entity 的字段时才创建。
+
+如果业务属于已有领域,例如 `system`,放到现有领域下;如果是独立业务领域,创建新的 `` 包名,例如 `business`。
+
+## 各层职责
+
+Controller 只负责 HTTP 层:
+
+- 声明 `@RestController`、`@RequestMapping`、`@Validated`、Swagger 注解。
+- 接收 `Command`、`Query`、路径参数和请求参数。
+- 使用 `@PreAuthorize("@permission.has('xxx:yyy:zzz')")` 做权限控制。
+- 对新增、修改、删除、导出等操作使用 `@AccessLog`。
+- 调用 `XxxApplicationService`。
+- 使用 `ResponseDTO.ok(...)` 返回结果。
+
+ApplicationService 负责编排业务用例:
+
+- 分页查询、详情查询、导出查询、新增、修改、删除。
+- 创建或加载 `XxxModel`。
+- 调用领域模型完成业务校验和状态变化。
+- 调用 `db` 包中的 Service 完成查询和持久化。
+- 将 Entity 或 DO 转换为 DTO。
+- 分页结果使用 `PageDTO`。
+
+Model 承载业务规则:
+
+- 从 `AddXxxCommand`、`UpdateXxxCommand` 加载字段。
+- 封装唯一性校验、状态校验、是否允许删除、业务状态流转等规则。
+- 需要数据库判断时,通过构造注入的 `XxxService` 查询。
+- 业务失败时抛出 `ApiException`,不要返回布尔值让上层解释。
+
+ModelFactory 负责创建和加载模型:
+
+- `create()` 返回带有依赖的空模型。
+- `loadById(id)` 从数据库加载 Entity,并包装为 Model。
+- 如果未找到必要数据,按项目现有异常风格处理。
+
+db 包只负责持久化:
+
+- `XxxEntity` 映射数据库表。
+- `XxxDO` 承载复杂查询结果,例如关联表字段、统计字段、报表字段。
+- `XxxMapper` 继承 MyBatis Plus `BaseMapper`。
+- `XxxService` 继承 `IService`,声明复用型查询方法。
+- `XxxServiceImpl` 继承 `ServiceImpl`,实现唯一性检查、关联检查、复杂查询等。
+- 复杂 SQL 放到 mapper XML,简单条件优先使用 MyBatis Plus wrapper。
+
+`XxxDO` 属于数据库查询结果对象,不是前端响应对象,也不是请求对象。Mapper 或 Service 可以返回 `XxxDO`,但 ApplicationService 必须转换为 `XxxDTO` 后再交给 Controller 返回。
+
+Command、Query、DTO 的边界:
+
+- `Command` 用于新增、修改等写请求,不要直接接收 Entity。
+- `Query` 用于列表、搜索、分页条件,并提供 `toPage()`、`toQueryWrapper()` 等项目已有风格的方法。
+- `DTO` 用于响应前端,不要直接暴露 Entity 或 DO。
+- 批量删除优先使用 `BulkOperationCommand`。
+
+## 开发流程
+
+1. 检索并阅读一个最相似的现有模块,确认命名、包路径、注解、分页、异常、测试风格。
+2. 确认数据库表结构,保持主键、软删除、审计字段、字段命名与现有表一致。
+3. 创建 `db` 包:`Entity`、`Mapper`、`Service`、`ServiceImpl`;如查询结果包含关联表字段、统计字段或报表字段,再增加 `XxxDO`。
+4. 创建 API 契约:`AddXxxCommand`、`UpdateXxxCommand`、`XxxQuery`、`XxxDTO`,需要详情时添加 `XxxDetailDTO`。
+5. 创建 `XxxModel` 和 `XxxModelFactory`,把业务校验放进 Model。
+6. 创建 `XxxApplicationService`,编排查询、新增、修改、删除、导出等用例。
+7. 创建 `XxxController`,加路由、权限、操作日志和统一响应。
+8. 如功能出现在后台菜单,补充权限标识、菜单 SQL 或清楚说明需要配置的权限码。
+9. 增加聚焦测试,至少覆盖核心业务校验和主要用例。
+10. 运行相关 Maven 测试或编译检查;如果不能运行,说明原因。
+
+开发过程中如果发现本文未覆盖的模式,优先参考现有模块,而不是引入新的架构风格。确实需要新增模式时,应先更新本文,再按新规范实现。
+
+## 权限、日志和错误
+
+权限码必须和前端菜单或按钮权限保持一致,格式参考现有系统功能:
+
+```java
+@PreAuthorize("@permission.has('system:post:add')")
+```
+
+操作日志按业务动作选择类型:
+
+```java
+@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.ADD)
+@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.MODIFY)
+@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.DELETE)
+@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.EXPORT)
+```
+
+业务错误使用 `ApiException` 和 `ErrorCode.Business`。新增业务错误时,按现有错误码结构补充枚举或常量,不要在 Controller 中拼接错误响应。
+
+## 验收清单
+
+- 新代码目录结构与相似模块一致。
+- Controller 只有 HTTP 编排逻辑。
+- 业务校验位于 Model 或 ApplicationService,不散落在前端或 Controller。
+- 查询返回 DTO,分页返回 `PageDTO`。
+- 写请求使用 Command。
+- 复杂查询结果对象使用 DO,并在 ApplicationService 转换为 DTO。
+- 权限、日志、异常、测试和现有项目风格一致。
diff --git a/docs/clean-code-contract.md b/docs/clean-code-contract.md
new file mode 100644
index 0000000..802e179
--- /dev/null
+++ b/docs/clean-code-contract.md
@@ -0,0 +1,275 @@
+# Clean Code 契约
+
+本文约束新增和修改代码时的代码质量形态。它只关注函数设计、命名、控制结构、错误处理、注释、副作用、重复和可读性,不替代前后端各自的架构开发规范。
+
+## 1. 函数设计
+
+- 新增或修改的函数默认不超过 20 行,不含空行和注释。
+- 业务编排函数最多不超过 30 行;超过时必须拆分。
+- 函数参数不超过 3 个;超过 3 个必须改为对象参数。
+- 圈复杂度 <= 5。
+- 嵌套深度 <= 2。
+- 必须优先使用 guard clause 提前返回,避免主流程被多层 `if` 包裹。
+- 一个函数只做一件事,不混合校验、转换、查询、保存、渲染等多个职责。
+
+Bad:
+
+```ts
+function submit(user, form, config, notify) {
+ if (user) {
+ if (form.valid) {
+ if (config.enabled) {
+ save(user, form);
+ notify.success();
+ }
+ }
+ }
+}
+```
+
+Good:
+
+```ts
+function submit(options: SubmitOptions) {
+ if (!options.user) return;
+ if (!options.form.valid) return;
+ if (!options.config.enabled) return;
+
+ save(options.user, options.form);
+ options.notify.success();
+}
+```
+
+## 2. 命名
+
+- 布尔变量必须以 `is`、`has`、`can`、`should` 开头。
+- 执行动作的函数使用动词开头,例如 `getUser`、`validateEmail`、`saveRole`。
+- 事件处理函数使用 `handle` 或 `on` 前缀,例如 `handleSubmit`、`onDialogClose`。
+- 有副作用的函数名必须体现动作,例如 `save`、`update`、`delete`、`load`、`fetch`、`sync`。
+- 避免无意义缩写。
+- 避免单字母变量,循环索引 `i` 除外。
+
+Bad:
+
+```ts
+const visible = user.status === "enabled";
+
+function userData() {
+ return api.getUser();
+}
+```
+
+Good:
+
+```ts
+const isVisible = user.status === "enabled";
+
+function getUserData() {
+ return api.getUser();
+}
+```
+
+## 3. 控制结构
+
+- 禁止深层 `if/else`。
+- 连续分支超过 3 个时,优先使用对象映射、`switch`、策略方法或枚举方法。
+- 简单数据转换优先使用 `map`、`filter`。
+- 不为了“函数式”强行使用难读的 `reduce`。
+- 当需要提前退出、复杂流程控制或显式循环更清晰时,允许使用 `for` / `for...of`。
+- 不允许在 `switch` 或长 `if/else` 中堆大量业务逻辑,复杂分支必须拆成独立函数。
+
+Bad:
+
+```ts
+function getStatusLabel(status: string) {
+ if (status === "enabled") return "启用";
+ if (status === "disabled") return "禁用";
+ if (status === "pending") return "待处理";
+ if (status === "locked") return "锁定";
+ return "未知";
+}
+```
+
+Good:
+
+```ts
+const statusLabels: Record = {
+ enabled: "启用",
+ disabled: "禁用",
+ pending: "待处理",
+ locked: "锁定"
+};
+
+function getStatusLabel(status: string) {
+ return statusLabels[status] ?? "未知";
+}
+```
+
+## 4. 错误处理
+
+- 禁止吞异常。
+- 禁止空 `catch`。
+- `catch` 后必须处理、记录、转换或重新抛出异常。
+- 外部调用必须有明确错误处理,包括网络、数据库、文件、缓存、第三方服务。
+- 不允许只打印错误但不中断、不反馈、不恢复状态。
+- 不允许把异常转换成无意义的 `null`、`false` 或空数组,除非调用方能明确区分该状态。
+
+Bad:
+
+```ts
+async function loadUser() {
+ try {
+ return await api.getUser();
+ } catch (error) {
+ console.log(error);
+ }
+}
+```
+
+Good:
+
+```ts
+async function loadUser() {
+ try {
+ return await api.getUser();
+ } catch (error) {
+ showErrorMessage("用户信息加载失败");
+ throw error;
+ }
+}
+```
+
+## 5. 注释
+
+- 禁止废话注释,例如 `i++ // 增加 i`。
+- 代码能清楚表达“做了什么”时,不写解释性注释。
+- 复杂算法、特殊兼容、业务规则例外必须说明“为什么这么做”。
+- 临时方案必须标明原因和后续处理方式,避免无上下文的 `TODO`。
+- 注释必须随代码更新,禁止保留过期注释。
+
+Bad:
+
+```ts
+// 遍历用户
+users.forEach(user => {
+ // 设置名称
+ user.name = user.name.trim();
+});
+```
+
+Good:
+
+```ts
+// 后端历史数据可能包含首尾空格,提交前统一清理,避免唯一性校验误判。
+const normalizedUsers = users.map(user => ({
+ ...user,
+ name: user.name.trim()
+}));
+```
+
+## 6. 副作用
+
+- 优先编写纯函数:相同输入应产生相同输出。
+- 数据转换、格式化、校验逻辑应尽量保持无副作用。
+- 修改外部状态的函数必须在命名中体现副作用。
+- 不在工具函数中偷偷修改入参、全局状态、缓存或组件状态。
+- 修改入参前必须显式说明原因;默认应返回新对象或新数组。
+
+Bad:
+
+```ts
+function normalizeUser(user: User) {
+ user.name = user.name.trim();
+ return user;
+}
+```
+
+Good:
+
+```ts
+function normalizeUser(user: User) {
+ return {
+ ...user,
+ name: user.name.trim()
+ };
+}
+```
+
+## 7. 重复与抽象
+
+- 禁止复制粘贴相同业务逻辑。
+- 出现第 2 次重复时可以接受局部重复。
+- 出现第 3 次重复时必须抽取公共函数、组件、常量或配置。
+- 不为了“预防未来变化”提前抽象。
+- 抽象必须基于已经出现的重复或明确稳定的业务概念。
+- 公共函数必须有清晰输入输出,不能依赖隐式上下文。
+
+Bad:
+
+```ts
+const userStatus = row.status === 1 ? "启用" : "禁用";
+const roleStatus = role.status === 1 ? "启用" : "禁用";
+const postStatus = post.status === 1 ? "启用" : "禁用";
+```
+
+Good:
+
+```ts
+function getEnabledStatusLabel(status: number) {
+ return status === 1 ? "启用" : "禁用";
+}
+
+const userStatus = getEnabledStatusLabel(row.status);
+const roleStatus = getEnabledStatusLabel(role.status);
+const postStatus = getEnabledStatusLabel(post.status);
+```
+
+## 8. 可读性
+
+- 优先让主流程从上到下阅读。
+- 变量声明尽量靠近使用位置。
+- 避免长链式调用;链式调用过长时拆成中间变量。
+- 避免布尔表达式过长;复杂条件必须提取为语义明确的变量或函数。
+- 避免魔法值;业务含义明确的数字、字符串应提取为常量。
+- 不写“聪明但难懂”的代码。
+
+Bad:
+
+```ts
+if ((user.age > 18 && user.status === 1 && !user.deleted) || user.role === "admin") {
+ allowAccess();
+}
+```
+
+Good:
+
+```ts
+const isActiveAdult = user.age > 18 && user.status === 1 && !user.deleted;
+const isAdmin = user.role === "admin";
+
+if (isActiveAdult || isAdmin) {
+ allowAccess();
+}
+```
+
+## 9. 自动化检查
+
+完成代码后必须运行与改动相关的检查。
+
+建议静态检查覆盖:
+
+- 函数长度
+- 参数数量
+- 圈复杂度
+- 嵌套深度
+- 空 `catch`
+- 未使用变量
+- 重复分支
+- 基础命名规则
+
+## 10. 例外规则
+
+- 如果确实需要突破本契约,必须说明原因。
+- 例外只能针对具体代码点,不能作为整个模块放宽标准的理由。
+- 为了满足行数限制而机械拆分、降低可读性的改动不接受。
+- 可读性优先于形式主义,但必须能解释为什么更清晰。
diff --git a/docs/web-feature-development.md b/docs/web-feature-development.md
new file mode 100644
index 0000000..569bba7
--- /dev/null
+++ b/docs/web-feature-development.md
@@ -0,0 +1,198 @@
+# Web 前端业务功能开发规范
+
+本文是 `frontend/web` 新增或修改前端业务功能时的权威规范。当前 Web 项目基于 Vue 3、Vite、Element Plus、Pure Admin、Pinia、Vue Router 和项目自有 `@/utils/http` 封装。开发时必须优先复用现有目录结构、组件和工具链。
+
+本文只覆盖 `frontend/web`,不覆盖 `frontend/app`。
+
+## 架构原则
+
+前端业务模块按“页面结构 + API 类型封装 + 业务 hook + 表单/弹窗 + 页面私有组件”组织。
+
+优先参考这些现有模块:
+
+- `frontend/web/src/views/system/post`
+- `frontend/web/src/views/system/user`
+- `frontend/web/src/views/system/role`
+- `frontend/web/src/views/system/notice`
+- `frontend/web/src/views/login`
+- `frontend/web/src/api/system/post.ts`
+
+不要绕过 `@/utils/http` 直接使用 Axios。不要在 `index.vue` 中堆积复杂列表逻辑。不要硬编码字典状态文本、值或标签样式。
+
+## 开发准备
+
+开发 `frontend/web` 业务功能前,应先确认页面所属业务域、后端接口、菜单路由、权限标识、字典依赖和相似页面。本文是 Web 前端业务功能开发的统一规范。
+
+开始编码前必须先做一次相似模块调研,确认目标功能最接近 `system/post`、`system/user`、`system/role`、`system/notice`、`login` 还是其他模块,再按相似模块的页面结构、API 类型、hook、表单、字典、表格、分页、排序、删除、导出、权限和组件命名风格实现。
+
+## 目录结构
+
+业务页面放在 `frontend/web/src/views///`:
+
+```text
+frontend/web/src/views///
+├── index.vue
+├── form.vue 或 xxx-form-modal.vue
+├── components/
+│ ├── private-panel.vue
+│ └── private-fragment.vue
+└── utils/
+ ├── hook.tsx
+ ├── rule.ts
+ └── types.ts
+```
+
+API 放在 `frontend/web/src/api//.ts`:
+
+```text
+frontend/web/src/api//.ts
+```
+
+`components/`、`rule.ts`、`types.ts` 按需创建,不要求每个模块都存在。
+
+## 页面私有组件
+
+只被某个页面或模块使用的组件,放在该模块目录下的 `components/`。现有示例是 `frontend/web/src/views/login/components`。
+
+页面私有组件适合承载:
+
+- 页面内局部展示组件。
+- 局部表单片段。
+- 局部抽屉、局部面板、局部弹窗。
+- 只服务当前页面流程的交互单元。
+
+页面私有组件不应承载整个业务模块的主流程。主列表、主表单、主弹窗仍按现有模块风格放在 `index.vue`、`form.vue` 或 `xxx-form-modal.vue`。
+
+如果组件开始被第二个业务模块复用,应提升到 `frontend/web/src/components`,不要继续放在某个页面私有目录中。只有在明确属于某个业务域且多个同域模块复用时,才考虑创建 `views//components`;当前项目还没有这个模式,新增前应谨慎。
+
+页面私有组件的判断标准是“是否只服务当前页面或当前模块”。如果组件包含通用交互、通用展示、通用上传、通用选择器等能力,应优先设计为公共组件,而不是藏在某个业务页面目录下。
+
+## 各文件职责
+
+`index.vue` 负责页面结构:
+
+- 搜索表单。
+- 表格和表格插槽。
+- 工具栏按钮。
+- 弹窗、抽屉、页面私有组件的挂载。
+- `defineOptions({ name })`,组件 name 应与菜单表中的 `router_name` 保持一致。
+
+`utils/hook.tsx` 负责列表业务状态和行为:
+
+- 查询参数、分页、排序、loading。
+- 表格列定义。
+- 列表查询、搜索、重置。
+- 删除、批量删除、导出。
+- 字典值渲染,例如 `el-tag`。
+
+`form.vue` 或 `xxx-form-modal.vue` 负责新增/编辑:
+
+- 接收 `v-model` 控制显示。
+- 接收 `type`、`row` 等必要 props。
+- 维护表单数据和校验状态。
+- 调用新增/修改 API。
+- 成功后关闭并向父组件发出 `success` 事件。
+
+`utils/rule.ts` 负责复杂或可复用的表单校验规则。简单模块也可以把规则放在表单组件内,保持与现有代码一致。
+
+`utils/types.ts` 只放页面私有类型。跨 API 复用的请求/响应类型优先放在对应 `src/api//.ts`。
+
+## API 规范
+
+每个业务模块在 `src/api//.ts` 中封装后端接口和 TypeScript 类型。
+
+接口调用使用项目 HTTP 封装:
+
+```ts
+http.request>("get", "/path", { params });
+http.request>("post", "/path", { data });
+```
+
+文件下载使用:
+
+```ts
+http.download("/path/excel", fileName, { params });
+```
+
+类型命名优先遵循现有风格:
+
+- `XxxListCommand`:列表查询参数。
+- `XxxPageResponse`:列表响应行。
+- `AddXxxCommand`:新增请求。
+- `UpdateXxxCommand`:修改请求。
+
+删除接口如果后端接收 `ids` 查询参数,按现有写法把数组转成字符串,避免 Axios 序列化为 `ids[0]`、`ids[1]`。
+
+## 列表、分页和排序
+
+列表页优先使用 `PureTableBar + pure-table`。
+
+分页使用 `PaginationProps`,并通过 `CommonUtils.fillPaginationParams()` 填充查询参数。
+
+排序使用 Element Plus `Sort`,并通过 `CommonUtils.fillSortParams()` 传给后端。
+
+时间范围查询使用 `beginTime`、`endTime`,优先复用现有 computed 写法处理 `el-date-picker` 的双向绑定。
+
+批量操作必须维护 `multipleSelection`,并在空选择时给出提示。
+
+## 字典和状态
+
+字典来自登录配置并缓存在用户 store 中。
+
+下拉选项使用:
+
+```ts
+useUserStoreHook().dictionaryList["common.status"]
+```
+
+表格状态渲染使用:
+
+```ts
+useUserStoreHook().dictionaryMap["common.status"]
+```
+
+不要在页面中硬编码状态 label、value、`el-tag` 类型。字典不存在或可能为空时,新增代码应避免直接访问导致运行时报错。
+
+## 交互和组件
+
+按钮图标优先使用 `useRenderIcon()` 和现有 iconify 图标。
+
+新增/编辑弹窗优先复用 `VDialog` 或相似模块的弹窗写法。
+
+单条删除使用 `el-popconfirm` 或模块现有风格。批量删除使用 `ElMessageBox.confirm` 时,必须处理取消分支并清理选择状态。
+
+操作成功后刷新列表。弹窗通过 `v-model` 和 `success` 事件与父组件通信。
+
+跨业务通用能力优先放在 `src/components` 或 `src/utils`;页面私有能力保持在页面目录内。
+
+## 权限、路由和菜单
+
+后台业务菜单主要依赖后端菜单和动态路由。新页面的组件 name 必须与菜单表中的 `router_name` 保持一致。
+
+不要随意新增静态路由。只有登录页、重定向、个人中心等固定页面才参考 `src/router/modules` 中的静态路由模式。
+
+如需要按钮权限,优先复用项目已有的 `ReAuth` 或 auth directive 模式,不要自行实现一套权限判断。
+
+## 验证清单
+
+前端改动后优先运行:
+
+```bash
+pnpm --dir frontend/web typecheck
+pnpm --dir frontend/web lint
+```
+
+如果依赖未安装或环境不允许运行,说明原因。
+
+如果只修改文档,不需要运行前端 typecheck 或 lint;如果修改了 TypeScript、Vue、样式或路由/API 文件,应优先运行上述检查。
+
+业务页面至少检查:
+
+- 列表加载。
+- 搜索和重置。
+- 分页和排序。
+- 新增和编辑。
+- 单条删除和批量删除。
+- 导出。
+- 字典下拉和表格状态展示。
+- 弹窗关闭、成功回调和列表刷新。
diff --git a/frontend/.prettierrc.cjs b/frontend/.prettierrc.cjs
index 87775f0..1fa2330 100644
--- a/frontend/.prettierrc.cjs
+++ b/frontend/.prettierrc.cjs
@@ -1,7 +1,7 @@
module.exports = {
+ tabWidth: 2,
bracketSpacing: true,
singleQuote: false,
arrowParens: "avoid",
trailingComma: "none"
};
-
diff --git a/frontend/app/.gitignore b/frontend/app/.gitignore
deleted file mode 100644
index bc64557..0000000
--- a/frontend/app/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-dist/
-deploy_versions/
-.temp/
-.rn_temp/
-node_modules/
-.DS_Store
-.swc
-*.local
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 7b3fe4c..504e864 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -204,6 +204,9 @@ importers:
nprogress:
specifier: ^0.2.0
version: 0.2.0
+ path:
+ specifier: ^0.12.7
+ version: 0.12.7
pinia:
specifier: ^2.1.4
version: 2.3.1(typescript@5.0.4)(vue@3.5.34(typescript@5.0.4))
@@ -3995,6 +3998,9 @@ packages:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+ inherits@2.0.3:
+ resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
+
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -4774,6 +4780,9 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
+ path@0.12.7:
+ resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==}
+
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
@@ -5350,6 +5359,10 @@ packages:
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+ process@0.11.10:
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+ engines: {node: '>= 0.6.0'}
+
promise-polyfill@7.1.2:
resolution: {integrity: sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==}
@@ -6216,6 +6229,9 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ util@0.10.4:
+ resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==}
+
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
@@ -10917,6 +10933,8 @@ snapshots:
once: 1.4.0
wrappy: 1.0.2
+ inherits@2.0.3: {}
+
inherits@2.0.4: {}
ini@1.3.8: {}
@@ -11649,6 +11667,11 @@ snapshots:
path-type@4.0.0: {}
+ path@0.12.7:
+ dependencies:
+ process: 0.11.10
+ util: 0.10.4
+
pathe@2.0.3:
optional: true
@@ -12160,6 +12183,8 @@ snapshots:
process-nextick-args@2.0.1: {}
+ process@0.11.10: {}
+
promise-polyfill@7.1.2: {}
property-expr@2.0.6: {}
@@ -13164,6 +13189,10 @@ snapshots:
util-deprecate@1.0.2: {}
+ util@0.10.4:
+ dependencies:
+ inherits: 2.0.3
+
uuid@8.3.2: {}
validate-html-nesting@1.2.4: {}
diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml
index 8bc57c7..1411e21 100644
--- a/frontend/pnpm-workspace.yaml
+++ b/frontend/pnpm-workspace.yaml
@@ -1,4 +1,13 @@
packages:
- "web"
- "app"
-
+allowBuilds:
+ '@parcel/watcher': set this to true or false
+ '@swc/core': set this to true or false
+ '@tarojs/binding': set this to true or false
+ '@tarojs/cli': set this to true or false
+ core-js: set this to true or false
+ core-js-pure: set this to true or false
+ esbuild: set this to true or false
+ typeit: set this to true or false
+ vue-demi: set this to true or false
diff --git a/frontend/web/.gitignore b/frontend/web/.gitignore
deleted file mode 100644
index 91361d9..0000000
--- a/frontend/web/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-node_modules
-.DS_Store
-dist
-dist-ssr
-*.local
-.eslintcache
-report.html
-
-yarn.lock
-npm-debug.log*
-.pnpm-error.log*
-.pnpm-debug.log
-tests/**/coverage/
-
-# 本机调试debug配置文件
-.vscode/launch.json
-
-# Editor directories and files
-.idea
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-tsconfig.tsbuildinfo
\ No newline at end of file
diff --git a/frontend/web/.vscode/extensions.json b/frontend/web/.vscode/extensions.json
deleted file mode 100644
index 069796d..0000000
--- a/frontend/web/.vscode/extensions.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "recommendations": [
- "akamud.vscode-theme-onedark",
- "antfu.iconify",
- "bradlc.vscode-tailwindcss",
- "christian-kohler.npm-intellisense",
- "christian-kohler.path-intellisense",
- "Codeium.codeium",
- "csstools.postcss",
- "DavidAnson.vscode-markdownlint",
- "dbaeumer.vscode-eslint",
- "donjayamanne.githistory",
- "dsznajder.es7-react-js-snippets",
- "eamodio.gitlens",
- "ecmel.vscode-html-css",
- "esbenp.prettier-vscode",
- "genieai.chatgpt-vscode",
- "hollowtree.vue-snippets",
- "lokalise.i18n-ally",
- "mhutchie.git-graph",
- "mikestead.dotenv",
- "pmneo.tsimporter",
- "streetsidesoftware.code-spell-checker",
- "stylelint.vscode-stylelint",
- "syler.sass-indented",
- "sysoev.language-stylus",
- "vscode-icons-team.vscode-icons",
- "Vue.volar",
- "xabikos.JavaScriptSnippets"
- ]
-}
diff --git a/frontend/web/.vscode/vue3.0.code-snippets b/frontend/web/.vscode/vue3.0.code-snippets
deleted file mode 100644
index bb43589..0000000
--- a/frontend/web/.vscode/vue3.0.code-snippets
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "Vue3.0快速生成模板": {
- "scope": "vue",
- "prefix": "Vue3.0",
- "body": [
- "",
- "\ttest
",
- "\n",
- "\n",
- "",
- "$2"
- ],
- "description": "Vue3.0"
- }
-}
diff --git a/frontend/web/.vscode/vue3.2.code-snippets b/frontend/web/.vscode/vue3.2.code-snippets
deleted file mode 100644
index 2cebb46..0000000
--- a/frontend/web/.vscode/vue3.2.code-snippets
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "Vue3.2+快速生成模板": {
- "scope": "vue",
- "prefix": "Vue3.2+",
- "body": [
- "\n",
- "",
- "\ttest
",
- "\n",
- "",
- "$2"
- ],
- "description": "Vue3.2+"
- }
-}
diff --git a/frontend/web/.vscode/vue3.3.code-snippets b/frontend/web/.vscode/vue3.3.code-snippets
deleted file mode 100644
index dc7a106..0000000
--- a/frontend/web/.vscode/vue3.3.code-snippets
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "Vue3.3+defineOptions快速生成模板": {
- "scope": "vue",
- "prefix": "Vue3.3+",
- "body": [
- "\n",
- "",
- "\ttest
",
- "\n",
- "",
- "$2"
- ],
- "description": "Vue3.3+defineOptions快速生成模板"
- }
-}
diff --git a/frontend/web/package.json b/frontend/web/package.json
index c1dad5b..ca8a05c 100644
--- a/frontend/web/package.json
+++ b/frontend/web/package.json
@@ -41,6 +41,7 @@
"jsencrypt": "^3.3.2",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
+ "path": "^0.12.7",
"pinia": "^2.1.4",
"pinyin-pro": "^3.15.2",
"cropperjs": "^1.5.13",
diff --git a/frontend/web/src/api/collaboration/record.ts b/frontend/web/src/api/collaboration/record.ts
new file mode 100644
index 0000000..e48d462
--- /dev/null
+++ b/frontend/web/src/api/collaboration/record.ts
@@ -0,0 +1,206 @@
+import { http } from "@/utils/http";
+
+export type SettlementStatusValue =
+ | "NONE"
+ | "SETTLED"
+ | "UNSETTLED"
+ | "PARTIAL";
+export type CollaborationFileType = "GOODS_IMAGE" | "ATTACHMENT";
+
+export interface SettlementStatusDTO {
+ status: SettlementStatusValue;
+ label: string;
+}
+
+export interface CollaborationRecordListCommand extends BasePageQuery {
+ brand?: string;
+ goods?: string;
+ cooperationPlatform?: string;
+ purchaseBeginTime?: string;
+ purchaseEndTime?: string;
+}
+
+export interface CollaborationTaskCommand {
+ releaseDate?: string;
+}
+
+export interface CollaborationExpenditureCommand {
+ spendDate?: string;
+ amount?: number;
+ purpose?: string;
+}
+
+export interface CollaborationSettlementCommand {
+ settleDate?: string;
+ method?: string;
+ income?: number;
+ purpose?: string;
+}
+
+export interface CollaborationFileCommand {
+ fileType: CollaborationFileType;
+ url: string;
+ fileName?: string;
+ newFileName?: string;
+ originalFilename?: string;
+}
+
+export interface AddCollaborationRecordCommand {
+ brand: string;
+ goods: string;
+ cooperationPlatform?: string;
+ imageReturnNum: number;
+ retainedMethod?: string;
+ cooperatedMethod?: string;
+ purchaseMethod?: string;
+ purchasePrice?: number;
+ purchaseDate?: string;
+ purchasePlatform?: string;
+ deadline?: string;
+ remuneration?: number;
+ completeDate?: string;
+ requirements?: string;
+ remark?: string;
+ tasks: CollaborationTaskCommand[];
+ expenditures: CollaborationExpenditureCommand[];
+ settlements: CollaborationSettlementCommand[];
+ files: CollaborationFileCommand[];
+}
+
+export interface UpdateCollaborationRecordCommand
+ extends AddCollaborationRecordCommand {
+ recordId: number;
+}
+
+export interface CollaborationRecordPageResponse {
+ recordId: number;
+ brand: string;
+ goods: string;
+ cooperationPlatform?: string;
+ imageReturnNum: number;
+ retainedMethod?: string;
+ cooperatedMethod?: string;
+ purchaseMethod?: string;
+ purchasePrice?: number;
+ purchaseDate?: string;
+ purchasePlatform?: string;
+ deadline?: string;
+ remuneration?: number;
+ completeDate?: string;
+ requirements?: string;
+ remark?: string;
+ tasksNum: number;
+ completedTasksNum: number;
+ purchaseSettlementStatus: SettlementStatusDTO;
+ deliverySettlementStatus: SettlementStatusDTO;
+ remunerationSettlementStatus: SettlementStatusDTO;
+ createTime: string;
+}
+
+export interface CollaborationRecordDetailResponse
+ extends CollaborationRecordPageResponse {
+ tasks: Array<
+ CollaborationTaskCommand & { taskId?: number; sortOrder?: number }
+ >;
+ expenditures: Array<
+ CollaborationExpenditureCommand & { expenditureId?: number }
+ >;
+ settlements: Array<
+ CollaborationSettlementCommand & { settlementId?: number }
+ >;
+ files: Array<
+ CollaborationFileCommand & { fileId?: number; sortOrder?: number }
+ >;
+}
+
+export interface CollaborationOptionResponse {
+ type: string;
+ label: string;
+ values: string[];
+}
+
+export interface CollaborationMonthlyStatisticsResponse {
+ month: number;
+ purchasePrice: number;
+ expenditureAmount: number;
+ settledRemuneration: number;
+ settledTotal: number;
+}
+
+export interface UploadResponse {
+ url: string;
+ fileName: string;
+ newFileName: string;
+ originalFilename: string;
+}
+
+export const getCollaborationRecordListApi = (
+ params: CollaborationRecordListCommand
+) => {
+ return http.request>>(
+ "get",
+ "/collaboration/record/list",
+ { params }
+ );
+};
+
+export const getCollaborationRecordInfoApi = (recordId: number) => {
+ return http.request>(
+ "get",
+ `/collaboration/record/${recordId}`
+ );
+};
+
+export const addCollaborationRecordApi = (
+ data: AddCollaborationRecordCommand
+) => {
+ return http.request>("post", "/collaboration/record", {
+ data
+ });
+};
+
+export const updateCollaborationRecordApi = (
+ data: UpdateCollaborationRecordCommand
+) => {
+ return http.request>("put", "/collaboration/record", {
+ data
+ });
+};
+
+export const deleteCollaborationRecordApi = (data: Array) => {
+ return http.request>("delete", "/collaboration/record", {
+ params: {
+ ids: data.toString()
+ }
+ });
+};
+
+export const getCollaborationOptionsApi = () => {
+ return http.request>(
+ "get",
+ "/collaboration/record/options"
+ );
+};
+
+export const getCollaborationMonthlyStatisticsApi = (year: number) => {
+ return http.request>(
+ "get",
+ "/collaboration/record/monthly-statistics",
+ {
+ params: { year }
+ }
+ );
+};
+
+export const uploadCollaborationFileApi = (data: FormData) => {
+ return http.request>(
+ "post",
+ "/file/upload",
+ { data },
+ {
+ headers: {
+ "Content-Type": "multipart/form-data"
+ }
+ }
+ );
+};
diff --git a/frontend/web/src/components/ReDialog/index.vue b/frontend/web/src/components/ReDialog/index.vue
index ee7a6be..0f64e2b 100644
--- a/frontend/web/src/components/ReDialog/index.vue
+++ b/frontend/web/src/components/ReDialog/index.vue
@@ -89,6 +89,7 @@ function handleClose(
:key="index"
v-bind="options"
v-model="options.visible"
+ :align-center="options.alignCenter ?? true"
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
@close="handleClose(options, index)"
@opened="eventsCallBack('open', options, index)"
diff --git a/frontend/web/src/components/VDialog/VDialog.vue b/frontend/web/src/components/VDialog/VDialog.vue
index 3f1812d..42ae580 100644
--- a/frontend/web/src/components/VDialog/VDialog.vue
+++ b/frontend/web/src/components/VDialog/VDialog.vue
@@ -40,7 +40,7 @@
class="header-btn"
/>