From 12313b0c1e00b98284b9b0e9b753921886743368 Mon Sep 17 00:00:00 2001 From: Yuhang Wu Date: Fri, 12 Jun 2026 17:38:14 +0800 Subject: [PATCH] =?UTF-8?q?```=20chore(project):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=9F=BA=E7=A1=80=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了项目根目录的 .gitignore 文件,包含前端、后端、IDE等各类忽略规则; 新增 README.md 项目说明文档,详细介绍了 PawTrace 服务端的技术栈、目录结构、 模块功能、快速开始指南和版本规划等内容; 初始化 admin-pawtrace 管理端的基础文件,包括 package.json 依赖配置、 index.html 入口文件和 favicon.svg 图标文件。 ``` --- .gitignore | 138 ++++++++ README.md | 187 +++++++++++ admin-pawtrace/index.html | 13 + admin-pawtrace/package.json | 38 +++ admin-pawtrace/public/favicon.svg | 1 + admin-pawtrace/public/icons.svg | 24 ++ admin-pawtrace/src/App.vue | 7 + admin-pawtrace/src/assets/hero.png | Bin 0 -> 13057 bytes admin-pawtrace/src/assets/vite.svg | 1 + admin-pawtrace/src/assets/vue.svg | 1 + admin-pawtrace/src/components/HelloWorld.vue | 95 ++++++ admin-pawtrace/src/main.ts | 5 + admin-pawtrace/src/style.css | 296 ++++++++++++++++++ admin-pawtrace/tsconfig.app.json | 14 + admin-pawtrace/tsconfig.json | 7 + admin-pawtrace/tsconfig.node.json | 24 ++ admin-pawtrace/vite.config.ts | 7 + server-pawtrace/pom.xml | 249 +++++++++++++++ .../server/ServerPawtraceApplication.java | 30 ++ .../common/constant/CommonConstants.java | 47 +++ .../server/common/constant/ResultCode.java | 47 +++ .../server/common/enums/DiaryTypeEnum.java | 23 ++ .../common/enums/LifeCardCategoryEnum.java | 23 ++ .../server/common/enums/PetGenderEnum.java | 21 ++ .../common/exception/BusinessException.java | 41 +++ .../exception/GlobalExceptionHandler.java | 67 ++++ .../server/common/page/PageRequest.java | 31 ++ .../server/common/page/PageResult.java | 52 +++ .../pawtrace/server/common/result/Result.java | 74 +++++ .../server/common/utils/DateUtils.java | 43 +++ .../pawtrace/server/config/AsyncConfig.java | 32 ++ .../pawtrace/server/config/Knife4jConfig.java | 28 ++ .../config/MybatisMetaObjectHandler.java | 28 ++ .../server/config/MybatisPlusConfig.java | 27 ++ .../pawtrace/server/config/RedisConfig.java | 43 +++ .../pawtrace/server/config/SaTokenConfig.java | 33 ++ .../com/pawtrace/server/config/WebConfig.java | 24 ++ .../pawtrace/server/entity/BaseEntity.java | 41 +++ .../diary/controller/DiaryController.java | 50 +++ .../server/modules/diary/entity/Diary.java | 52 +++ .../modules/diary/mapper/DiaryMapper.java | 14 + .../modules/diary/service/DiaryService.java | 19 ++ .../diary/service/impl/DiaryServiceImpl.java | 35 +++ .../controller/FeedbackController.java | 28 ++ .../modules/feedback/entity/Feedback.java | 39 +++ .../feedback/mapper/FeedbackMapper.java | 8 + .../feedback/service/FeedbackService.java | 6 + .../service/impl/FeedbackServiceImpl.java | 10 + .../file/controller/FileController.java | 66 ++++ .../health/controller/HealthController.java | 61 ++++ .../server/modules/health/entity/Deworm.java | 55 ++++ .../server/modules/health/entity/Vaccine.java | 55 ++++ .../modules/health/entity/VisitRecord.java | 49 +++ .../modules/health/mapper/DewormMapper.java | 8 + .../modules/health/mapper/VaccineMapper.java | 10 + .../health/mapper/VisitRecordMapper.java | 8 + .../modules/health/service/HealthService.java | 17 + .../service/impl/HealthServiceImpl.java | 44 +++ .../controller/InventoryController.java | 57 ++++ .../modules/inventory/entity/Inventory.java | 49 +++ .../inventory/entity/InventoryLog.java | 37 +++ .../inventory/mapper/InventoryLogMapper.java | 8 + .../inventory/mapper/InventoryMapper.java | 9 + .../inventory/service/InventoryService.java | 16 + .../service/impl/InventoryServiceImpl.java | 46 +++ .../controller/LifeCardController.java | 39 +++ .../modules/lifecard/entity/LifeCard.java | 55 ++++ .../lifecard/mapper/LifeCardMapper.java | 8 + .../lifecard/service/LifeCardService.java | 12 + .../service/impl/LifeCardServiceImpl.java | 27 ++ .../modules/pet/controller/PetController.java | 47 +++ .../server/modules/pet/entity/Pet.java | 55 ++++ .../server/modules/pet/entity/PetWeight.java | 43 +++ .../server/modules/pet/mapper/PetMapper.java | 15 + .../modules/pet/mapper/PetWeightMapper.java | 14 + .../modules/pet/service/PetService.java | 22 ++ .../modules/pet/service/PetWeightService.java | 12 + .../pet/service/impl/PetServiceImpl.java | 45 +++ .../service/impl/PetWeightServiceImpl.java | 16 + .../scheduled/ReminderScheduledTask.java | 67 ++++ .../controller/TimeMachineController.java | 54 ++++ .../timemachine/entity/TimeMachineWork.java | 48 +++ .../mapper/TimeMachineWorkMapper.java | 8 + .../service/TimeMachineWorkService.java | 6 + .../impl/TimeMachineWorkServiceImpl.java | 10 + .../user/controller/AuthController.java | 35 +++ .../server/modules/user/entity/User.java | 45 +++ .../modules/user/mapper/UserMapper.java | 14 + .../modules/user/service/UserService.java | 17 + .../user/service/impl/UserServiceImpl.java | 22 ++ .../src/main/resources/application-dev.yml | 9 + .../src/main/resources/application-prod.yml | 10 + .../src/main/resources/application.yml | 125 ++++++++ .../src/main/resources/sql/pawtrace.sql | 251 +++++++++++++++ uniapp-pawtrace/.npmrc | 3 + uniapp-pawtrace/eslint.config.js | 3 + uniapp-pawtrace/index.html | 21 ++ uniapp-pawtrace/manifest.config.ts | 80 +++++ uniapp-pawtrace/package.json | 89 ++++++ uniapp-pawtrace/pages.config.ts | 16 + uniapp-pawtrace/src/App.ku.vue | 47 +++ uniapp-pawtrace/src/App.vue | 9 + uniapp-pawtrace/src/components/AppFooter.vue | 24 ++ uniapp-pawtrace/src/components/AppLogos.vue | 26 ++ uniapp-pawtrace/src/components/InputEntry.vue | 45 +++ uniapp-pawtrace/src/main.ts | 16 + uniapp-pawtrace/src/manifest.json | 70 +++++ uniapp-pawtrace/src/pages.json | 19 ++ uniapp-pawtrace/src/pages/index.vue | 13 + uniapp-pawtrace/src/static/github.svg | 1 + uniapp-pawtrace/src/static/logo.svg | 1 + uniapp-pawtrace/src/static/vite.png | Bin 0 -> 7685 bytes uniapp-pawtrace/src/stores/counter.ts | 10 + uniapp-pawtrace/src/theme.json | 26 ++ uniapp-pawtrace/src/uni.scss | 76 +++++ uniapp-pawtrace/test/index.test.ts | 15 + uniapp-pawtrace/tsconfig.json | 49 +++ uniapp-pawtrace/unh.config.ts | 21 ++ uniapp-pawtrace/uno.config.ts | 26 ++ uniapp-pawtrace/vite.config.ts | 67 ++++ uniapp-pawtrace/vitest.config.ts | 14 + 121 files changed, 4636 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 admin-pawtrace/index.html create mode 100644 admin-pawtrace/package.json create mode 100644 admin-pawtrace/public/favicon.svg create mode 100644 admin-pawtrace/public/icons.svg create mode 100644 admin-pawtrace/src/App.vue create mode 100644 admin-pawtrace/src/assets/hero.png create mode 100644 admin-pawtrace/src/assets/vite.svg create mode 100644 admin-pawtrace/src/assets/vue.svg create mode 100644 admin-pawtrace/src/components/HelloWorld.vue create mode 100644 admin-pawtrace/src/main.ts create mode 100644 admin-pawtrace/src/style.css create mode 100644 admin-pawtrace/tsconfig.app.json create mode 100644 admin-pawtrace/tsconfig.json create mode 100644 admin-pawtrace/tsconfig.node.json create mode 100644 admin-pawtrace/vite.config.ts create mode 100644 server-pawtrace/pom.xml create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/ServerPawtraceApplication.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/constant/CommonConstants.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/constant/ResultCode.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/enums/DiaryTypeEnum.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/enums/LifeCardCategoryEnum.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/enums/PetGenderEnum.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/exception/BusinessException.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/exception/GlobalExceptionHandler.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageRequest.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageResult.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/result/Result.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/common/utils/DateUtils.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/AsyncConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/Knife4jConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisMetaObjectHandler.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisPlusConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/RedisConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/SaTokenConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/config/WebConfig.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/entity/BaseEntity.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/controller/DiaryController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/entity/Diary.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/mapper/DiaryMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/DiaryService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/impl/DiaryServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/controller/FeedbackController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/entity/Feedback.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/mapper/FeedbackMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/FeedbackService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/impl/FeedbackServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/file/controller/FileController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/controller/HealthController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Deworm.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Vaccine.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/VisitRecord.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/DewormMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VaccineMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VisitRecordMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/HealthService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/impl/HealthServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/controller/InventoryController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/Inventory.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/InventoryLog.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryLogMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/InventoryService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/impl/InventoryServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/controller/LifeCardController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/entity/LifeCard.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/mapper/LifeCardMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/LifeCardService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/impl/LifeCardServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/controller/PetController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/Pet.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/PetWeight.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetWeightMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetWeightService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetWeightServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/reminder/scheduled/ReminderScheduledTask.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/controller/TimeMachineController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/entity/TimeMachineWork.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/mapper/TimeMachineWorkMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/TimeMachineWorkService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/impl/TimeMachineWorkServiceImpl.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/user/controller/AuthController.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/user/entity/User.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/user/mapper/UserMapper.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/UserService.java create mode 100644 server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/impl/UserServiceImpl.java create mode 100644 server-pawtrace/src/main/resources/application-dev.yml create mode 100644 server-pawtrace/src/main/resources/application-prod.yml create mode 100644 server-pawtrace/src/main/resources/application.yml create mode 100644 server-pawtrace/src/main/resources/sql/pawtrace.sql create mode 100644 uniapp-pawtrace/.npmrc create mode 100644 uniapp-pawtrace/eslint.config.js create mode 100644 uniapp-pawtrace/index.html create mode 100644 uniapp-pawtrace/manifest.config.ts create mode 100644 uniapp-pawtrace/package.json create mode 100644 uniapp-pawtrace/pages.config.ts create mode 100644 uniapp-pawtrace/src/App.ku.vue create mode 100644 uniapp-pawtrace/src/App.vue create mode 100644 uniapp-pawtrace/src/components/AppFooter.vue create mode 100644 uniapp-pawtrace/src/components/AppLogos.vue create mode 100644 uniapp-pawtrace/src/components/InputEntry.vue create mode 100644 uniapp-pawtrace/src/main.ts create mode 100644 uniapp-pawtrace/src/manifest.json create mode 100644 uniapp-pawtrace/src/pages.json create mode 100644 uniapp-pawtrace/src/pages/index.vue create mode 100644 uniapp-pawtrace/src/static/github.svg create mode 100644 uniapp-pawtrace/src/static/logo.svg create mode 100644 uniapp-pawtrace/src/static/vite.png create mode 100644 uniapp-pawtrace/src/stores/counter.ts create mode 100644 uniapp-pawtrace/src/theme.json create mode 100644 uniapp-pawtrace/src/uni.scss create mode 100644 uniapp-pawtrace/test/index.test.ts create mode 100644 uniapp-pawtrace/tsconfig.json create mode 100644 uniapp-pawtrace/unh.config.ts create mode 100644 uniapp-pawtrace/uno.config.ts create mode 100644 uniapp-pawtrace/vite.config.ts create mode 100644 uniapp-pawtrace/vitest.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cdb2421 --- /dev/null +++ b/.gitignore @@ -0,0 +1,138 @@ +# ============================================================ +# PawTrace 项目根 .gitignore +# 子模块: admin-pawtrace (Vite+TS) / uniapp-pawtrace (Uniapp) +# server-pawtrace (Spring Boot) +# ============================================================ + +# -------------------- 通用 -------------------- +# 日志 +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# 临时/缓存/备份 +*.tmp +*.bak +*.swp +*.swo +*.orig +.cache/ +.temp/ +.tmp/ + +# 系统文件 +.DS_Store +.DS_* +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# 环境/密钥(绝不入库) +.env +.env.* +!.env.example +*.pem +*.key +*.p12 +*.keystore +credentials.json +secrets.yaml + +# 测试覆盖率 +coverage/ +*.lcov +.nyc_output/ + +# -------------------- 前端通用 -------------------- +# 依赖 +node_modules/ +jspm_packages/ +bower_components/ + +# 构建产物 +dist/ +dist-ssr/ +build/ +out/ +.next/ +.nuxt/ +.vite/ +.parcel-cache/ +.turbo/ +.turbo-cache/ + +# TypeScript +*.tsbuildinfo +.tscache/ + +# -------------------- 管理端 admin-pawtrace (Vite) -------------------- +.vite/ +*.local +.vite-cache/ + +# -------------------- uniapp-pawtrace -------------------- +# HBuilderX +.idea/ +*.hbuilderx +HBuilderX.config +.project +unpackage/dist/ +unpackage/release/ +unpackage/cache/ +unpackage/resources/ + +# 微信开发者工具产物 +project.private.config.json +miniprogram_npm/ + +# -------------------- 服务端 server-pawtrace (Maven) -------------------- +target/ +build/ +out/ +*.class +*.jar +*.war +*.ear +hs_err_pid* +replay_pid* + +# Maven Wrapper +.mvn/wrapper/maven-wrapper.jar +!/.mvn/wrapper/maven-wrapper.properties + +# IDE - IntelliJ +.idea/ +*.iml +*.iws +*.ipr +out/ + +# IDE - Eclipse / STS +.settings/ +.classpath +.project +.factorypath +.springBeans +.sts4-cache/ +bin/ + +# IDE - VSCode(保留 settings.json / 推荐插件) +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +!.vscode/launch.json + +# IDE - NetBeans +nbproject/ +nbbuild/ +nbdist/ + +# 上传文件(本地运行时生成) +upload/ +uploads/ +static/upload/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c9e3f5 --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ +# 宠迹 PawTrace · 服务端 + +> 宠物成长记录与生活管理后端项目 +> 配套小程序: [`uniapp-pawtrace`](../uniapp-pawtrace) +> 配套管理端: [`admin-pawtrace`](../admin-pawtrace) + +## 一、项目简介 + +**宠迹(PawTrace)** 是一款面向个人宠物主的本地化、合规化宠物记录工具。服务端不参与任何用户间互动、不存储敏感人脸/位置信息,所有图片/视频处理均在客户端完成,服务端只负责元数据存取与提醒触达。 + +### 设计原则 + +- ✅ **本地优先**:服务端只做"陪伴",核心数据存本地,服务端兜底 +- ✅ **合规底线**:无 UGC 公开、无评论点赞、无支付、无地图 UGC 标注 +- ✅ **云端可选**:基础版可纯本地使用,登录后提供云端同步与多端访问 + +## 二、技术栈 + +| 类别 | 选型 | +|----------|--------------------------------------------| +| 基础框架 | Spring Boot 4.0.6 / Java 21 | +| 持久层 | MyBatis-Plus 3.5.15 + MySQL 8.x | +| 缓存 | Redis (Lettuce) | +| 鉴权 | Sa-Token 1.45.x | +| 工具集 | Hutool 5.8.x / Fastjson2 | +| 对象映射 | MapStruct 1.6.x | +| 微信生态 | WxJava 4.8.x (小程序 / 订阅消息) | +| 对象存储 | 七牛云 / 阿里云 OSS / 腾讯云 COS / MinIO / AWS S3 | +| 接口文档 | Knife4j 4.6.x (OpenAPI 3) | +| 构建 | Maven 3.9+ | + +## 三、目录结构 + +``` +server-pawtrace +├── pom.xml +├── README.md +└── src/main + ├── java/com/pawtrace/server + │ ├── ServerPawtraceApplication.java # 启动类 + │ ├── common + │ │ ├── constant/ # 常量 (CommonConstants / ResultCode) + │ │ ├── enums/ # 枚举 (宠物性别 / 日记类型 / 信息卡分类) + │ │ ├── exception/ # 全局异常 (BusinessException / GlobalExceptionHandler) + │ │ ├── page/ # 分页 (PageRequest / PageResult) + │ │ ├── result/ # 统一返回 Result + │ │ └── utils/ # 工具类 + │ ├── config # 配置类 (MyBatis Plus / Redis / Sa-Token / 异步 / 跨域 / Knife4j) + │ ├── entity # 基础实体 (BaseEntity) + │ └── modules # 业务模块 + │ ├── user # 用户与鉴权 + │ ├── pet # 宠物档案 + 体重历史 + │ ├── diary # 成长日记 + │ ├── health # 健康医疗(疫苗/驱虫/就诊) + │ ├── inventory # 物资管家 + │ ├── timemachine # 时光机工坊(仅元数据) + │ ├── lifecard # 宠物生活信息卡 + │ ├── feedback # 意见反馈 + │ ├── reminder # 提醒调度 (Scheduled) + │ └── file # 文件上传 + └── resources + ├── application.yml # 主配置 + ├── application-dev.yml # 开发环境 + ├── application-prod.yml # 生产环境 + ├── mapper/ # MyBatis XML(可空) + └── sql/pawtrace.sql # 初始化 SQL +``` + +## 四、模块与功能映射 + +| 模块 | 路径前缀 | 对应功能设计章节 | 核心接口 | +|-------------------||-------------|--------------------------------------------------------| +| 用户/鉴权 | `/auth` | 一、个人中心 | 微信登录 / 登出 | +| 宠物档案 | `/pet` | 一、宠物档案 | CRUD + 体重历史分页 | +| 成长日记 | `/diary` | 二、成长日记 | 时间轴分页(按年/月/类型/宠物筛选) | +| 健康医疗 | `/health` | 三、健康医疗本 | 疫苗 / 驱虫 / 就诊 CRUD + 提醒开关 | +| 物资管家 | `/inventory` | 四、物资管家 | CRUD + 低库存预警 + 消耗记录 | +| 时光机工坊 | `/timemachine` | 五、时光机工坊 | 保存作品元数据(实际处理在客户端) | +| 宠物生活信息卡 | `/life-card` | 六、生活信息卡 | 按分类查询 + 详情 | +| 意见反馈 | `/feedback` | 一、个人中心 | 提交反馈 | +| 提醒调度 | (内部 Scheduled) | 三、提醒功能 | 每天 08:00 扫描即将到期的疫苗/驱虫 | +| 文件上传 | `/file` | (通用) | 单文件上传,本地存储,生产可切换 OSS | + +> **未实现功能(明确排除)**:用户间互动、私信、评论点赞、UGC 地图标注、电商/支付、AI 医疗诊断 —— 详见 [功能设计.md § 附录](../功能设计.md)。 + +## 五、快速开始 + +### 1. 环境准备 + +- JDK 21+ +- Maven 3.9+ +- MySQL 8.x +- Redis 7.x(可选,登录态需要) + +### 2. 初始化数据库 + +```bash +mysql -u root -p < src/main/resources/sql/pawtrace.sql +``` + +### 3. 修改配置 + +编辑 `application-dev.yml`,修改 MySQL/Redis 账号密码及 `application.yml` 中的微信小程序 `appid/secret`、OSS 配置。 + +### 4. 启动 + +```bash +# 方式一:IDE 直接运行 ServerPawtraceApplication +# 方式二:命令行 +mvn spring-boot:run +# 方式三:打包运行 +mvn clean package -DskipTests +java -jar target/server-pawtrace-0.0.1.jar +``` + +启动成功后: + +- 接口地址: +- 接口文档: + +## 六、核心约定 + +### 1. 统一返回 + +```json +{ + "code": 200, + "message": "操作成功", + "data": { ... }, + "timestamp": 1718000000000 +} +``` + +业务码详见 [ResultCode.java](src/main/java/com/pawtrace/server/common/constant/ResultCode.java)。 + +### 2. 鉴权 + +使用 Sa-Token,前端在请求头携带 `Authorization: `(由 `/auth/login` 返回)。除以下白名单外,所有接口需要登录: + +``` +/auth/login /auth/logout /file/** +/doc.html /swagger-ui.html /v3/api-docs/** +``` + +### 3. 业务约束 + +- 宠物上限:`MAX_PET_COUNT = 10` +- 单条日记图片上限:`MAX_DIARY_IMAGES = 9` +- 时光机视频图片上限:`MAX_TIMEMACHINE_IMAGES = 15` +- 提醒提前天数:支持 `1` / `3` / `7` 天 + +### 4. 数据隔离 + +所有业务表都带 `user_id` 字段,Service 层强制按当前登录用户过滤,严禁出现跨用户读取。 + +## 七、扩展指引 + +| 想要做的事 | 修改点 | +|------------------|----------------------------------------------------------------------------| +| 切换到 OSS | 实现 `OssService` 接口,替换 `FileController` 中的 `file.transferTo(...)` | +| 新增宠物种类 | 扩展 `Pet.species` 枚举值与 `PetGenderEnum` 等枚举 | +| 新增日记类型 | 扩展 `DiaryTypeEnum` | +| 接入实际的微信订阅消息推送 | `ReminderScheduledTask#scanUpcomingReminders` 的 `TODO` 位置,调用 `wx-java` 推送 | +| 接入管理端 | 现有接口已经支持按 userId 过滤,管理端只需另写一个 `ROLE_ADMIN` 路由 + 后台 `Controller` | +| 关闭/开启 WebSocket | `pom.xml` 已预留 `spring-boot-starter-websocket`,业务需要时编写 `WebSocket` Handler | + +## 八、版本规划 + +- **v0.0.1(当前)**:项目骨架 + 基础 CRUD + 鉴权 + 提醒任务占位 +- **v0.1.0**:微信登录接入、订阅消息推送、文件存储切换 OSS +- **v0.2.0**:管理端接口、统计接口、数据备份/恢复 +- **v1.0.0**:多端同步、家庭共享(可选) + +## 九、许可证 + +MIT License. 详见 [LICENSE](../LICENSE)(如有)。 + +## 十、贡献 + +1. Fork 本仓库 +2. 在 `feature/xxx` 分支开发 +3. 提交 PR 前请保证 `mvn clean compile` 通过 +4. 提交信息请遵循 `feat: ...` / `fix: ...` / `docs: ...` 规范 + +--- + +> Made with ❤️ by PawTrace Team diff --git a/admin-pawtrace/index.html b/admin-pawtrace/index.html new file mode 100644 index 0000000..e44c436 --- /dev/null +++ b/admin-pawtrace/index.html @@ -0,0 +1,13 @@ + + + + + + + admin-pawtrace + + +
+ + + diff --git a/admin-pawtrace/package.json b/admin-pawtrace/package.json new file mode 100644 index 0000000..21d728a --- /dev/null +++ b/admin-pawtrace/package.json @@ -0,0 +1,38 @@ +{ + "name": "admin-pawtrace", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.34", + "axios": "^1.17.0", + "pinia": "^3.0.4", + "vue-router": "^5.1.0", + "ant-design-vue": "~4.2.6", + "@ant-design/icons-vue": "^7.0.1", + "@vueuse/core": "^14.3.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^1.0.2", + "echarts": "^6.1.0", + "nprogress": "^0.2.0", + "pinia-plugin-persistedstate": "^4.7.1" + }, + "devDependencies": { + "@types/node": "^24.12.3", + "@vitejs/plugin-vue": "^6.0.6", + "@vue/tsconfig": "^0.9.1", + "typescript": "~6.0.2", + "vite": "^8.0.12", + "vue-tsc": "^3.2.8", + "@iconify/vue": "^5.0.1", + "sass": "^1.101.0", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^32.1.0", + "vite-plugin-vue-devtools": "^8.1.2" + } +} diff --git a/admin-pawtrace/public/favicon.svg b/admin-pawtrace/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/admin-pawtrace/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin-pawtrace/public/icons.svg b/admin-pawtrace/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/admin-pawtrace/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin-pawtrace/src/App.vue b/admin-pawtrace/src/App.vue new file mode 100644 index 0000000..388b05e --- /dev/null +++ b/admin-pawtrace/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/admin-pawtrace/src/assets/hero.png b/admin-pawtrace/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..02251f4b956c55af2d76fd0788124d7eee2b45eb GIT binary patch literal 13057 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 P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/admin-pawtrace/src/assets/vite.svg b/admin-pawtrace/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/admin-pawtrace/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/admin-pawtrace/src/assets/vue.svg b/admin-pawtrace/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/admin-pawtrace/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin-pawtrace/src/components/HelloWorld.vue b/admin-pawtrace/src/components/HelloWorld.vue new file mode 100644 index 0000000..c232865 --- /dev/null +++ b/admin-pawtrace/src/components/HelloWorld.vue @@ -0,0 +1,95 @@ + + + diff --git a/admin-pawtrace/src/main.ts b/admin-pawtrace/src/main.ts new file mode 100644 index 0000000..2425c0f --- /dev/null +++ b/admin-pawtrace/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/admin-pawtrace/src/style.css b/admin-pawtrace/src/style.css new file mode 100644 index 0000000..527d4fb --- /dev/null +++ b/admin-pawtrace/src/style.css @@ -0,0 +1,296 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} + +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/admin-pawtrace/tsconfig.app.json b/admin-pawtrace/tsconfig.app.json new file mode 100644 index 0000000..5c750c5 --- /dev/null +++ b/admin-pawtrace/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/admin-pawtrace/tsconfig.json b/admin-pawtrace/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/admin-pawtrace/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/admin-pawtrace/tsconfig.node.json b/admin-pawtrace/tsconfig.node.json new file mode 100644 index 0000000..d3c52ea --- /dev/null +++ b/admin-pawtrace/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/admin-pawtrace/vite.config.ts b/admin-pawtrace/vite.config.ts new file mode 100644 index 0000000..bbcf80c --- /dev/null +++ b/admin-pawtrace/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/server-pawtrace/pom.xml b/server-pawtrace/pom.xml new file mode 100644 index 0000000..d5b935c --- /dev/null +++ b/server-pawtrace/pom.xml @@ -0,0 +1,249 @@ + + + 4.0.0 + com.pawtrace + server-pawtrace + 0.0.1 + server-pawtrace + 宠迹服务端 - 宠物成长记录与生活管理 + + + 21 + UTF-8 + UTF-8 + 4.0.6 + 3.5.15 + 5.8.44 + 1.45.0 + 4.6.0 + 2.0.62 + 2.0.61 + 7.19.0 + 2.44.4 + 9.0.0 + 3.18.5 + 5.6.269 + 4.8.3.B + 1.6.3 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.security + spring-security-crypto + + + + com.baomidou + mybatis-plus-spring-boot4-starter + + + + com.baomidou + mybatis-plus-jsqlparser-4.9 + + + + com.mysql + mysql-connector-j + + + + cn.hutool + hutool-all + ${hutool.version} + + + + cn.dev33 + sa-token-spring-boot4-starter + ${sa-token.version} + + + + org.projectlombok + lombok + + + + com.github.xingfudeshi + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + compile + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + + + com.alibaba.fastjson2 + fastjson2-extension-spring6 + ${fastjson2-extension.version} + + + + com.qiniu + qiniu-java-sdk + ${qiniu.version} + + + software.amazon.awssdk + s3 + ${s3.version} + + + io.minio + minio + ${minio.version} + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun-oss.version} + + + com.qcloud + cos_api + ${tencent-cos.version} + + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + ${weixin-java.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + com.baomidou + mybatis-plus-bom + ${mybatis-plus.version} + pom + import + + + + + + + + src/main/resources + + + src/main/java + + **/mapper/*.xml + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 21 + 21 + UTF-8 + + -parameters + + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.pawtrace.server.ServerPawtraceApplication + + + + repackage + + repackage + + + + + + + + + + huaweicloud + huawei + https://mirrors.huaweicloud.com/repository/maven/ + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/ServerPawtraceApplication.java b/server-pawtrace/src/main/java/com/pawtrace/server/ServerPawtraceApplication.java new file mode 100644 index 0000000..327022f --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/ServerPawtraceApplication.java @@ -0,0 +1,30 @@ +package com.pawtrace.server; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 宠迹服务端启动类 + * + * @author PawTrace Team + */ +@EnableAsync +@EnableScheduling +@SpringBootApplication +@MapperScan("com.pawtrace.server.mapper") +public class ServerPawtraceApplication { + + public static void main(String[] args) { + SpringApplication.run(ServerPawtraceApplication.class, args); + System.out.println(""" + + ==================================================== + 宠迹 PawTrace 服务端启动成功 + Knife4j: http://localhost:8080/api/doc.html + ==================================================== + """); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/CommonConstants.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/CommonConstants.java new file mode 100644 index 0000000..5477d68 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/CommonConstants.java @@ -0,0 +1,47 @@ +package com.pawtrace.server.common.constant; + +/** + * 通用常量 + * + * @author PawTrace Team + */ +public final class CommonConstants { + + private CommonConstants() { + } + + /** 通用标识 */ + public static final String DEFAULT = "default"; + public static final String ROOT = "0"; + public static final Long ROOT_ID = 0L; + + /** 逻辑删除标识 */ + public static final Integer DELETED = 1; + public static final Integer NOT_DELETED = 0; + + /** 状态:启用/禁用 */ + public static final Integer STATUS_ENABLED = 1; + public static final Integer STATUS_DISABLED = 0; + + /** 是/否 */ + public static final Integer YES = 1; + public static final Integer NO = 0; + + /** 分页默认值 */ + public static final Long DEFAULT_PAGE_NUM = 1L; + public static final Long DEFAULT_PAGE_SIZE = 10L; + public static final Long MAX_PAGE_SIZE = 200L; + + /** 业务限制 */ + public static final Integer MAX_PET_COUNT = 10; + public static final Integer MAX_DIARY_IMAGES = 9; + public static final Integer MAX_TIMEMACHINE_IMAGES = 15; + + /** Header */ + public static final String HEADER_AUTHORIZATION = "Authorization"; + public static final String HEADER_USER_ID = "X-User-Id"; + + /** 角色标识 */ + public static final String ROLE_USER = "user"; + public static final String ROLE_ADMIN = "admin"; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/ResultCode.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/ResultCode.java new file mode 100644 index 0000000..46eea87 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/constant/ResultCode.java @@ -0,0 +1,47 @@ +package com.pawtrace.server.common.constant; + +import lombok.Getter; + +/** + * 业务返回码枚举 + * + * @author PawTrace Team + */ +@Getter +public enum ResultCode { + + SUCCESS(200, "操作成功"), + FAIL(500, "操作失败"), + + BAD_REQUEST(400, "请求参数错误"), + UNAUTHORIZED(401, "未登录或登录已过期"), + FORBIDDEN(403, "无权访问"), + NOT_FOUND(404, "资源不存在"), + METHOD_NOT_ALLOWED(405, "请求方法不允许"), + + // 业务相关 1xxx + USER_NOT_FOUND(1001, "用户不存在"), + USER_ALREADY_EXISTS(1002, "用户已存在"), + LOGIN_FAILED(1003, "登录失败"), + TOKEN_INVALID(1004, "令牌无效"), + + PET_NOT_FOUND(2001, "宠物不存在"), + PET_LIMIT_EXCEEDED(2002, "宠物数量已达上限"), + + DIARY_NOT_FOUND(3001, "日记不存在"), + + HEALTH_RECORD_NOT_FOUND(4001, "健康记录不存在"), + + INVENTORY_NOT_FOUND(5001, "物资不存在"), + + FILE_UPLOAD_FAILED(9001, "文件上传失败"), + FILE_TYPE_INVALID(9002, "文件类型不支持"); + + private final Integer code; + private final String message; + + ResultCode(Integer code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/DiaryTypeEnum.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/DiaryTypeEnum.java new file mode 100644 index 0000000..b3ff0b7 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/DiaryTypeEnum.java @@ -0,0 +1,23 @@ +package com.pawtrace.server.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 日记类型 + * + * @author PawTrace Team + */ +@Getter +@AllArgsConstructor +public enum DiaryTypeEnum { + + DAILY(1, "日常"), + GROWTH(2, "成长"), + MEDICAL(3, "医疗"), + DIET(4, "饮食"), + FUN(5, "趣事"); + + private final Integer code; + private final String desc; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/LifeCardCategoryEnum.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/LifeCardCategoryEnum.java new file mode 100644 index 0000000..dfa90a8 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/LifeCardCategoryEnum.java @@ -0,0 +1,23 @@ +package com.pawtrace.server.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 生活信息卡分类 + * + * @author PawTrace Team + */ +@Getter +@AllArgsConstructor +public enum LifeCardCategoryEnum { + + HOSPITAL(1, "宠物医院"), + RESTAURANT(2, "宠物友好餐厅"), + PARK(3, "宠物公园"), + STORE(4, "宠物店"), + GROOMING(5, "宠物美容"); + + private final Integer code; + private final String desc; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/PetGenderEnum.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/PetGenderEnum.java new file mode 100644 index 0000000..0134202 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/enums/PetGenderEnum.java @@ -0,0 +1,21 @@ +package com.pawtrace.server.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 宠物性别 + * + * @author PawTrace Team + */ +@Getter +@AllArgsConstructor +public enum PetGenderEnum { + + UNKNOWN(0, "未知"), + MALE(1, "公"), + FEMALE(2, "母"); + + private final Integer code; + private final String desc; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/BusinessException.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/BusinessException.java new file mode 100644 index 0000000..efabab7 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/BusinessException.java @@ -0,0 +1,41 @@ +package com.pawtrace.server.common.exception; + +import com.pawtrace.server.common.constant.ResultCode; +import lombok.Getter; + +import java.io.Serial; + +/** + * 业务异常 + * + * @author PawTrace Team + */ +@Getter +public class BusinessException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** 业务错误码 */ + private final Integer code; + + public BusinessException(String message) { + super(message); + this.code = ResultCode.FAIL.getCode(); + } + + public BusinessException(Integer code, String message) { + super(message); + this.code = code; + } + + public BusinessException(ResultCode resultCode) { + super(resultCode.getMessage()); + this.code = resultCode.getCode(); + } + + public BusinessException(ResultCode resultCode, String message) { + super(message); + this.code = resultCode.getCode(); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/GlobalExceptionHandler.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..30670cf --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,67 @@ +package com.pawtrace.server.common.exception; + +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.common.constant.ResultCode; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.stream.Collectors; + +/** + * 全局异常处理器 + * + * @author PawTrace Team + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(BusinessException.class) + public Result handleBusinessException(BusinessException e, HttpServletRequest request) { + log.warn("业务异常 [{}] {} -> {}", e.getCode(), request.getRequestURI(), e.getMessage()); + return Result.fail(e.getCode(), e.getMessage()); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result handleValidException(MethodArgumentNotValidException e) { + String msg = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining("; ")); + log.warn("参数校验失败: {}", msg); + return Result.fail(ResultCode.BAD_REQUEST.getCode(), msg); + } + + @ExceptionHandler(BindException.class) + public Result handleBindException(BindException e) { + String msg = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining("; ")); + return Result.fail(ResultCode.BAD_REQUEST.getCode(), msg); + } + + @ExceptionHandler(MissingServletRequestParameterException.class) + public Result handleMissingParam(MissingServletRequestParameterException e) { + return Result.fail(ResultCode.BAD_REQUEST.getCode(), "缺少参数: " + e.getParameterName()); + } + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseEntity> handleMethodNotSupported(HttpRequestMethodNotSupportedException e) { + return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED) + .body(Result.fail(ResultCode.METHOD_NOT_ALLOWED.getCode(), e.getMessage())); + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception e, HttpServletRequest request) { + log.error("系统异常 [{}] {}", request.getRequestURI(), e.getMessage(), e); + return Result.fail(ResultCode.FAIL.getCode(), "系统繁忙,请稍后再试"); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageRequest.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageRequest.java new file mode 100644 index 0000000..c8f3c14 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageRequest.java @@ -0,0 +1,31 @@ +package com.pawtrace.server.common.page; + +import com.pawtrace.server.common.constant.CommonConstants; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 分页请求 + * + * @author PawTrace Team + */ +@Data +public class PageRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 当前页 */ + private Long pageNum = CommonConstants.DEFAULT_PAGE_NUM; + + /** 每页大小 */ + private Long pageSize = CommonConstants.DEFAULT_PAGE_SIZE; + + /** 排序字段 */ + private String orderBy; + + /** 排序方式: asc / desc */ + private String order = "desc"; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageResult.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageResult.java new file mode 100644 index 0000000..87ca7a7 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/page/PageResult.java @@ -0,0 +1,52 @@ +package com.pawtrace.server.common.page; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +/** + * 分页结果 + * + * @param 数据类型 + * @author PawTrace Team + */ +@Data +public class PageResult implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private Long total; + + /** 当前页 */ + private Long pageNum; + + /** 每页大小 */ + private Long pageSize; + + /** 总页数 */ + private Long pages; + + /** 数据列表 */ + private List records; + + public PageResult() { + this.records = Collections.emptyList(); + } + + public PageResult(Long total, Long pageNum, Long pageSize, List records) { + this.total = total; + this.pageNum = pageNum; + this.pageSize = pageSize; + this.pages = (total + pageSize - 1) / pageSize; + this.records = records; + } + + public static PageResult empty(Long pageNum, Long pageSize) { + return new PageResult<>(0L, pageNum, pageSize, Collections.emptyList()); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/result/Result.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/result/Result.java new file mode 100644 index 0000000..da3c694 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/result/Result.java @@ -0,0 +1,74 @@ +package com.pawtrace.server.common.result; + +import com.pawtrace.server.common.constant.ResultCode; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 统一返回结果 + * + * @param 数据类型 + */ +@Data +public class Result implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 业务状态码 */ + private Integer code; + + /** 提示信息 */ + private String message; + + /** 业务数据 */ + private T data; + + /** 时间戳 */ + private Long timestamp; + + public Result() { + this.timestamp = System.currentTimeMillis(); + } + + public Result(Integer code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + this.timestamp = System.currentTimeMillis(); + } + + public static Result success() { + return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null); + } + + public static Result success(T data) { + return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); + } + + public static Result success(String message, T data) { + return new Result<>(ResultCode.SUCCESS.getCode(), message, data); + } + + public static Result fail() { + return new Result<>(ResultCode.FAIL.getCode(), ResultCode.FAIL.getMessage(), null); + } + + public static Result fail(String message) { + return new Result<>(ResultCode.FAIL.getCode(), message, null); + } + + public static Result fail(Integer code, String message) { + return new Result<>(code, message, null); + } + + public static Result fail(ResultCode resultCode) { + return new Result<>(resultCode.getCode(), resultCode.getMessage(), null); + } + + public boolean isSuccess() { + return ResultCode.SUCCESS.getCode().equals(this.code); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/common/utils/DateUtils.java b/server-pawtrace/src/main/java/com/pawtrace/server/common/utils/DateUtils.java new file mode 100644 index 0000000..8ec1612 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/common/utils/DateUtils.java @@ -0,0 +1,43 @@ +package com.pawtrace.server.common.utils; + +import com.pawtrace.server.common.constant.CommonConstants; + +import java.time.LocalDate; +import java.time.Period; +import java.time.format.DateTimeFormatter; + +/** + * 日期工具 + * + * @author PawTrace Team + */ +public final class DateUtils { + + private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private DateUtils() { + } + + /** + * 根据生日计算年龄(年/月) + */ + public static String calculateAge(LocalDate birthday) { + if (birthday == null) { + return "未知"; + } + LocalDate now = LocalDate.now(); + if (birthday.isAfter(now)) { + return "未出生"; + } + Period period = Period.between(birthday, now); + if (period.getYears() <= 0) { + return period.getMonths() + "个月"; + } + return period.getYears() + "岁" + (period.getMonths() > 0 ? period.getMonths() + "个月" : ""); + } + + public static String format(LocalDate date) { + return date == null ? CommonConstants.DEFAULT : date.format(DATE_FORMAT); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/AsyncConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/AsyncConfig.java new file mode 100644 index 0000000..215635d --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/AsyncConfig.java @@ -0,0 +1,32 @@ +package com.pawtrace.server.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 异步任务 / 线程池配置 + * + * @author PawTrace Team + */ +@Configuration +@EnableAsync +public class AsyncConfig { + + @Bean("pawtraceTaskExecutor") + public Executor pawtraceTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(32); + executor.setQueueCapacity(500); + executor.setKeepAliveSeconds(60); + executor.setThreadNamePrefix("pawtrace-async-"); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/Knife4jConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/Knife4jConfig.java new file mode 100644 index 0000000..e44f476 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/Knife4jConfig.java @@ -0,0 +1,28 @@ +package com.pawtrace.server.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Knife4j / OpenAPI 配置 + * + * @author PawTrace Team + */ +@Configuration +public class Knife4jConfig { + + @Bean + public OpenAPI pawtraceOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("宠迹 PawTrace 服务端 API") + .description("宠物成长记录与生活管理后端接口文档") + .version("v1.0.0") + .contact(new Contact().name("PawTrace Team").email("dev@pawtrace.com")) + .license(new License().name("MIT").url("https://opensource.org/licenses/MIT"))); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisMetaObjectHandler.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisMetaObjectHandler.java new file mode 100644 index 0000000..d6bafba --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisMetaObjectHandler.java @@ -0,0 +1,28 @@ +package com.pawtrace.server.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * MyBatis Plus 自动填充处理器 + * + * @author PawTrace Team + */ +@Component +public class MybatisMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); + this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); + this.strictInsertFill(metaObject, "deleted", Integer.class, 0); + } + + @Override + public void updateFill(MetaObject metaObject) { + this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisPlusConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisPlusConfig.java new file mode 100644 index 0000000..e17e323 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/MybatisPlusConfig.java @@ -0,0 +1,27 @@ +package com.pawtrace.server.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * MyBatis Plus 配置 + * + * @author PawTrace Team + */ +@Configuration +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + // 乐观锁插件 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/RedisConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/RedisConfig.java new file mode 100644 index 0000000..b948d7c --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/RedisConfig.java @@ -0,0 +1,43 @@ +package com.pawtrace.server.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * Redis 配置 + * + * @author PawTrace Team + */ +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + + ObjectMapper om = new ObjectMapper(); + om.registerModule(new JavaTimeModule()); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + + Jackson2JsonRedisSerializer jsonSerializer = new Jackson2JsonRedisSerializer<>(om, Object.class); + StringRedisSerializer stringSerializer = new StringRedisSerializer(); + + template.setKeySerializer(stringSerializer); + template.setHashKeySerializer(stringSerializer); + template.setValueSerializer(jsonSerializer); + template.setHashValueSerializer(jsonSerializer); + template.afterPropertiesSet(); + return template; + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/SaTokenConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/SaTokenConfig.java new file mode 100644 index 0000000..108f7a1 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/SaTokenConfig.java @@ -0,0 +1,33 @@ +package com.pawtrace.server.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Sa-Token 路由拦截配置 + * + * @author PawTrace Team + */ +@Configuration +public class SaTokenConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new SaInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns( + "/auth/login", + "/auth/logout", + "/doc.html", + "/swagger-ui.html", + "/swagger-resources/**", + "/webjars/**", + "/v3/api-docs/**", + "/favicon.ico", + "/file/**" + ); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/config/WebConfig.java b/server-pawtrace/src/main/java/com/pawtrace/server/config/WebConfig.java new file mode 100644 index 0000000..d54554a --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/config/WebConfig.java @@ -0,0 +1,24 @@ +package com.pawtrace.server.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Web 配置(CORS、静态资源等) + * + * @author PawTrace Team + */ +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") + .allowedHeaders("*") + .allowCredentials(true) + .maxAge(3600); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/entity/BaseEntity.java b/server-pawtrace/src/main/java/com/pawtrace/server/entity/BaseEntity.java new file mode 100644 index 0000000..8c7d735 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/entity/BaseEntity.java @@ -0,0 +1,41 @@ +package com.pawtrace.server.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 基础实体 + * + * @author PawTrace Team + */ +@Data +public class BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "create_time", fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @Schema(description = "逻辑删除标识 0未删 1已删") + @TableLogic + @TableField("deleted") + private Integer deleted; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/controller/DiaryController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/controller/DiaryController.java new file mode 100644 index 0000000..d9b7b0b --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/controller/DiaryController.java @@ -0,0 +1,50 @@ +package com.pawtrace.server.modules.diary.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.page.PageRequest; +import com.pawtrace.server.common.page.PageResult; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.diary.entity.Diary; +import com.pawtrace.server.modules.diary.service.DiaryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +/** + * 成长日记 Controller + * + * @author PawTrace Team + */ +@Tag(name = "成长日记") +@RestController +@RequestMapping("/diary") +public class DiaryController { + + private final DiaryService diaryService; + + public DiaryController(DiaryService diaryService) { + this.diaryService = diaryService; + } + + @Operation(summary = "时间轴分页") + @GetMapping("/page") + public Result> page(@RequestParam(required = false) Long petId, + @RequestParam(required = false) Integer diaryType, + @RequestParam(required = false) String yearMonth, + PageRequest request) { + return Result.success(diaryService.pageTimeline(StpUtil.getLoginIdAsLong(), petId, diaryType, yearMonth, request)); + } + + @Operation(summary = "新增/更新日记") + @PostMapping("/save") + public Result save(@RequestBody Diary diary) { + diary.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(diaryService.saveOrUpdate(diary)); + } + + @Operation(summary = "删除日记") + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(diaryService.removeById(id)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/entity/Diary.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/entity/Diary.java new file mode 100644 index 0000000..71dc43e --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/entity/Diary.java @@ -0,0 +1,52 @@ +package com.pawtrace.server.modules.diary.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.time.LocalDate; + +/** + * 成长日记 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_diary") +@Schema(description = "成长日记") +public class Diary extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "关联宠物ID") + private Long petId; + + @Schema(description = "标题") + private String title; + + @Schema(description = "内容") + private String content; + + @Schema(description = "媒体URL列表(JSON数组字符串)") + private String mediaUrls; + + @Schema(description = "心情标签") + private String moodTags; + + @Schema(description = "地点文本") + private String location; + + @Schema(description = "记录类型 1日常 2成长 3医疗 4饮食 5趣事") + private Integer diaryType; + + @Schema(description = "发生日期") + private LocalDate happenDate; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/mapper/DiaryMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/mapper/DiaryMapper.java new file mode 100644 index 0000000..6741c54 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/mapper/DiaryMapper.java @@ -0,0 +1,14 @@ +package com.pawtrace.server.modules.diary.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.diary.entity.Diary; +import org.apache.ibatis.annotations.Mapper; + +/** + * 日记 Mapper + * + * @author PawTrace Team + */ +@Mapper +public interface DiaryMapper extends BaseMapper { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/DiaryService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/DiaryService.java new file mode 100644 index 0000000..a518d90 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/DiaryService.java @@ -0,0 +1,19 @@ +package com.pawtrace.server.modules.diary.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.common.page.PageRequest; +import com.pawtrace.server.common.page.PageResult; +import com.pawtrace.server.modules.diary.entity.Diary; + +/** + * 日记 Service + * + * @author PawTrace Team + */ +public interface DiaryService extends IService { + + /** + * 时间轴分页(支持年份/月份/类型/宠物筛选) + */ + PageResult pageTimeline(Long userId, Long petId, Integer diaryType, String yearMonth, PageRequest request); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/impl/DiaryServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/impl/DiaryServiceImpl.java new file mode 100644 index 0000000..289dfb4 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/diary/service/impl/DiaryServiceImpl.java @@ -0,0 +1,35 @@ +package com.pawtrace.server.modules.diary.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.common.page.PageRequest; +import com.pawtrace.server.common.page.PageResult; +import com.pawtrace.server.modules.diary.entity.Diary; +import com.pawtrace.server.modules.diary.mapper.DiaryMapper; +import com.pawtrace.server.modules.diary.service.DiaryService; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +/** + * 日记 Service 实现 + * + * @author PawTrace Team + */ +@Service +public class DiaryServiceImpl extends ServiceImpl implements DiaryService { + + @Override + public PageResult pageTimeline(Long userId, Long petId, Integer diaryType, String yearMonth, PageRequest request) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Diary::getUserId, userId) + .eq(petId != null, Diary::getPetId, petId) + .eq(diaryType != null, Diary::getDiaryType, diaryType) + .likeRight(StringUtils.hasText(yearMonth), Diary::getHappenDate, yearMonth) // 形如 2025-06 + .orderByDesc(Diary::getHappenDate, Diary::getCreateTime); + + IPage page = this.page(new Page<>(request.getPageNum(), request.getPageSize()), wrapper); + return new PageResult<>(page.getTotal(), request.getPageNum(), request.getPageSize(), page.getRecords()); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/controller/FeedbackController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/controller/FeedbackController.java new file mode 100644 index 0000000..0adce96 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/controller/FeedbackController.java @@ -0,0 +1,28 @@ +package com.pawtrace.server.modules.feedback.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.feedback.entity.Feedback; +import com.pawtrace.server.modules.feedback.service.FeedbackService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "意见反馈") +@RestController +@RequestMapping("/feedback") +public class FeedbackController { + + private final FeedbackService feedbackService; + + public FeedbackController(FeedbackService feedbackService) { + this.feedbackService = feedbackService; + } + + @Operation(summary = "提交反馈") + @PostMapping("/submit") + public Result submit(@RequestBody Feedback fb) { + fb.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(feedbackService.save(fb)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/entity/Feedback.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/entity/Feedback.java new file mode 100644 index 0000000..3746571 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/entity/Feedback.java @@ -0,0 +1,39 @@ +package com.pawtrace.server.modules.feedback.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 用户意见反馈 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_feedback") +@Schema(description = "意见反馈") +public class Feedback extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "反馈类型 1建议 2问题 3其他") + private Integer fbType; + + @Schema(description = "内容") + private String content; + + @Schema(description = "联系方式(可选)") + private String contact; + + @Schema(description = "图片URL(JSON数组)") + private String images; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/mapper/FeedbackMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/mapper/FeedbackMapper.java new file mode 100644 index 0000000..ca315a5 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/mapper/FeedbackMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.feedback.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.feedback.entity.Feedback; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface FeedbackMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/FeedbackService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/FeedbackService.java new file mode 100644 index 0000000..0d7e8b4 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/FeedbackService.java @@ -0,0 +1,6 @@ +package com.pawtrace.server.modules.feedback.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.feedback.entity.Feedback; + +public interface FeedbackService extends IService {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/impl/FeedbackServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/impl/FeedbackServiceImpl.java new file mode 100644 index 0000000..f90fbfb --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/feedback/service/impl/FeedbackServiceImpl.java @@ -0,0 +1,10 @@ +package com.pawtrace.server.modules.feedback.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.modules.feedback.entity.Feedback; +import com.pawtrace.server.modules.feedback.mapper.FeedbackMapper; +import com.pawtrace.server.modules.feedback.service.FeedbackService; +import org.springframework.stereotype.Service; + +@Service +public class FeedbackServiceImpl extends ServiceImpl implements FeedbackService {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/file/controller/FileController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/file/controller/FileController.java new file mode 100644 index 0000000..cab18e9 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/file/controller/FileController.java @@ -0,0 +1,66 @@ +package com.pawtrace.server.modules.file.controller; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.IdUtil; +import com.pawtrace.server.common.constant.CommonConstants; +import com.pawtrace.server.common.constant.ResultCode; +import com.pawtrace.server.common.exception.BusinessException; +import com.pawtrace.server.common.result.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * 文件上传 Controller + *

+ * 当前实现为本地存储,生产环境可切换到 阿里云 OSS / 腾讯云 COS / 七牛云 / MinIO / S3。 + * + * @author PawTrace Team + */ +@Slf4j +@Tag(name = "文件上传") +@RestController +@RequestMapping("/file") +public class FileController { + + @Value("${oss.local.path:./upload/}") + private String localPath; + + @Value("${oss.local.domain:http://localhost:8080/api/file/}") + private String domain; + + @Operation(summary = "上传单个文件") + @PostMapping("/upload") + public Result upload(@RequestParam("file") MultipartFile file) throws IOException { + if (file.isEmpty()) { + throw new BusinessException(ResultCode.FILE_UPLOAD_FAILED); + } + String original = file.getOriginalFilename(); + String ext = original != null && original.contains(".") + ? original.substring(original.lastIndexOf('.')) + : ""; + String dateDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")); + String filename = IdUtil.simpleUUID() + ext; + File target = new File(localPath + dateDir + "/" + filename); + FileUtil.mkParentDirs(target); + file.transferTo(target); + String url = domain + dateDir + "/" + filename; + log.info("文件上传: {} -> {}", original, url); + return Result.success(url); + } + + @Operation(summary = "静态资源回显(本地存储)") + @GetMapping("/**") + public void staticResource() { + // 由 WebConfig 的静态资源映射处理 + throw new BusinessException(ResultCode.NOT_FOUND); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/controller/HealthController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/controller/HealthController.java new file mode 100644 index 0000000..eaa7224 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/controller/HealthController.java @@ -0,0 +1,61 @@ +package com.pawtrace.server.modules.health.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.health.entity.Vaccine; +import com.pawtrace.server.modules.health.entity.Deworm; +import com.pawtrace.server.modules.health.entity.VisitRecord; +import com.pawtrace.server.modules.health.service.HealthService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +/** + * 健康医疗本 Controller + * + * @author PawTrace Team + */ +@Tag(name = "健康医疗") +@RestController +@RequestMapping("/health") +public class HealthController { + + private final HealthService healthService; + + public HealthController(HealthService healthService) { + this.healthService = healthService; + } + + @Operation(summary = "新增/更新疫苗") + @PostMapping("/vaccine/save") + public Result saveVaccine(@RequestBody Vaccine v) { + v.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(healthService.vaccine().saveOrUpdate(v)); + } + + @Operation(summary = "删除疫苗") + @DeleteMapping("/vaccine/{id}") + public Result delVaccine(@PathVariable Long id) { + return Result.success(healthService.vaccine().removeById(id)); + } + + @Operation(summary = "新增/更新驱虫") + @PostMapping("/deworm/save") + public Result saveDeworm(@RequestBody Deworm d) { + d.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(healthService.deworm().saveOrUpdate(d)); + } + + @Operation(summary = "删除驱虫") + @DeleteMapping("/deworm/{id}") + public Result delDeworm(@PathVariable Long id) { + return Result.success(healthService.deworm().removeById(id)); + } + + @Operation(summary = "新增/更新就诊") + @PostMapping("/visit/save") + public Result saveVisit(@RequestBody VisitRecord v) { + v.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(healthService.visit().saveOrUpdate(v)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Deworm.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Deworm.java new file mode 100644 index 0000000..adf4baa --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Deworm.java @@ -0,0 +1,55 @@ +package com.pawtrace.server.modules.health.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.time.LocalDate; + +/** + * 驱虫记录 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_deworm") +@Schema(description = "驱虫记录") +public class Deworm extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "宠物ID") + private Long petId; + + @Schema(description = "驱虫药名称") + private String drugName; + + @Schema(description = "类型 1体内 2体外") + private Integer drugType; + + @Schema(description = "用药日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate useDate; + + @Schema(description = "下次用药日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate nextDate; + + @Schema(description = "包装照片") + private String drugImage; + + @Schema(description = "提醒开关") + private Integer reminderEnabled; + + @Schema(description = "提前提醒天数") + private Integer remindBeforeDays; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Vaccine.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Vaccine.java new file mode 100644 index 0000000..7ed0e66 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/Vaccine.java @@ -0,0 +1,55 @@ +package com.pawtrace.server.modules.health.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.time.LocalDate; + +/** + * 疫苗记录 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_vaccine") +@Schema(description = "疫苗记录") +public class Vaccine extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "宠物ID") + private Long petId; + + @Schema(description = "疫苗名称") + private String vaccineName; + + @Schema(description = "接种日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate injectDate; + + @Schema(description = "下次接种日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate nextDate; + + @Schema(description = "疫苗本照片URL") + private String certImage; + + @Schema(description = "提醒 0关闭 1开启") + private Integer reminderEnabled; + + @Schema(description = "提前提醒天数 1/3/7") + private Integer remindBeforeDays; + + @Schema(description = "备注") + private String remark; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/VisitRecord.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/VisitRecord.java new file mode 100644 index 0000000..5276ec2 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/entity/VisitRecord.java @@ -0,0 +1,49 @@ +package com.pawtrace.server.modules.health.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 就诊记录 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_visit_record") +@Schema(description = "就诊记录") +public class VisitRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "宠物ID") + private Long petId; + + @Schema(description = "医院名称") + private String hospital; + + @Schema(description = "就诊原因") + private String reason; + + @Schema(description = "诊断结果") + private String diagnosis; + + @Schema(description = "费用(可选)") + private BigDecimal cost; + + @Schema(description = "就诊日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate visitDate; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/DewormMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/DewormMapper.java new file mode 100644 index 0000000..aec99cf --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/DewormMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.health.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.health.entity.Deworm; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface DewormMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VaccineMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VaccineMapper.java new file mode 100644 index 0000000..0e1f910 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VaccineMapper.java @@ -0,0 +1,10 @@ +package com.pawtrace.server.modules.health.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.health.entity.Vaccine; +import com.pawtrace.server.modules.health.entity.Deworm; +import com.pawtrace.server.modules.health.entity.VisitRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface VaccineMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VisitRecordMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VisitRecordMapper.java new file mode 100644 index 0000000..3c5a6b2 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/mapper/VisitRecordMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.health.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.health.entity.VisitRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface VisitRecordMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/HealthService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/HealthService.java new file mode 100644 index 0000000..702142d --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/HealthService.java @@ -0,0 +1,17 @@ +package com.pawtrace.server.modules.health.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.health.entity.Vaccine; +import com.pawtrace.server.modules.health.entity.Deworm; +import com.pawtrace.server.modules.health.entity.VisitRecord; + +/** + * 健康模块聚合 Service(疫苗/驱虫/就诊) + * + * @author PawTrace Team + */ +public interface HealthService { + IService vaccine(); + IService deworm(); + IService visit(); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/impl/HealthServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/impl/HealthServiceImpl.java new file mode 100644 index 0000000..9492b82 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/health/service/impl/HealthServiceImpl.java @@ -0,0 +1,44 @@ +package com.pawtrace.server.modules.health.service.impl; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.modules.health.entity.Vaccine; +import com.pawtrace.server.modules.health.entity.Deworm; +import com.pawtrace.server.modules.health.entity.VisitRecord; +import com.pawtrace.server.modules.health.mapper.VaccineMapper; +import com.pawtrace.server.modules.health.mapper.DewormMapper; +import com.pawtrace.server.modules.health.mapper.VisitRecordMapper; +import com.pawtrace.server.modules.health.service.HealthService; +import org.springframework.stereotype.Service; + +/** + * 健康 Service 聚合(可由多个 Service 拆分,此处为统一入口) + * + * @author PawTrace Team + */ +@Service +public class HealthServiceImpl implements HealthService { + + private final VaccineServiceImpl vaccineService; + private final DewormServiceImpl dewormService; + private final VisitRecordServiceImpl visitService; + + public HealthServiceImpl(VaccineServiceImpl vaccineService, + DewormServiceImpl dewormService, + VisitRecordServiceImpl visitService) { + this.vaccineService = vaccineService; + this.dewormService = dewormService; + this.visitService = visitService; + } + + @Override public IService vaccine() { return vaccineService; } + @Override public IService deworm() { return dewormService; } + @Override public IService visit() { return visitService; } + + @Service + public static class VaccineServiceImpl extends ServiceImpl {} + @Service + public static class DewormServiceImpl extends ServiceImpl {} + @Service + public static class VisitRecordServiceImpl extends ServiceImpl {} +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/controller/InventoryController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/controller/InventoryController.java new file mode 100644 index 0000000..bdfb442 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/controller/InventoryController.java @@ -0,0 +1,57 @@ +package com.pawtrace.server.modules.inventory.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.inventory.entity.Inventory; +import com.pawtrace.server.modules.inventory.entity.InventoryLog; +import com.pawtrace.server.modules.inventory.service.InventoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "物资管家") +@RestController +@RequestMapping("/inventory") +public class InventoryController { + + private final InventoryService inventoryService; + + public InventoryController(InventoryService inventoryService) { + this.inventoryService = inventoryService; + } + + @Operation(summary = "我的物资列表") + @GetMapping("/list") + public Result> list() { + Long uid = StpUtil.getLoginIdAsLong(); + return Result.success(inventoryService.lambdaQuery().eq(Inventory::getUserId, uid).list()); + } + + @Operation(summary = "新增/更新物资") + @PostMapping("/save") + public Result save(@RequestBody Inventory inv) { + inv.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(inventoryService.saveOrUpdate(inv)); + } + + @Operation(summary = "低库存预警列表") + @GetMapping("/low-stock") + public Result> lowStock() { + return Result.success(inventoryService.listLowStock(StpUtil.getLoginIdAsLong())); + } + + @Operation(summary = "记录一次消耗") + @PostMapping("/consume") + public Result consume(@RequestBody InventoryLog log) { + log.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(inventoryService.consume(log)); + } + + @Operation(summary = "删除物资") + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(inventoryService.removeById(id)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/Inventory.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/Inventory.java new file mode 100644 index 0000000..7cb1883 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/Inventory.java @@ -0,0 +1,49 @@ +package com.pawtrace.server.modules.inventory.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 物资库存 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_inventory") +@Schema(description = "物资库存") +public class Inventory extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "分类 1粮 2罐头 3零食 4猫砂 5药品 6其他") + private Integer category; + + @Schema(description = "名称") + private String name; + + @Schema(description = "当前库存") + private BigDecimal stock; + + @Schema(description = "单位 g/ml/袋/盒") + private String unit; + + @Schema(description = "低库存阈值") + private BigDecimal lowStockThreshold; + + @Schema(description = "封面图") + private String coverImage; + + @Schema(description = "备注") + private String remark; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/InventoryLog.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/InventoryLog.java new file mode 100644 index 0000000..2396cf3 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/entity/InventoryLog.java @@ -0,0 +1,37 @@ +package com.pawtrace.server.modules.inventory.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 物资消耗记录 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_inventory_log") +@Schema(description = "物资消耗记录") +public class InventoryLog extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "物资ID") + private Long inventoryId; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "消耗数量(正数)") + private BigDecimal quantity; + + @Schema(description = "备注 如:吃了半袋") + private String remark; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryLogMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryLogMapper.java new file mode 100644 index 0000000..3bcd0f0 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryLogMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.inventory.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.inventory.entity.InventoryLog; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface InventoryLogMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryMapper.java new file mode 100644 index 0000000..edc77c3 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/mapper/InventoryMapper.java @@ -0,0 +1,9 @@ +package com.pawtrace.server.modules.inventory.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.inventory.entity.Inventory; +import com.pawtrace.server.modules.inventory.entity.InventoryLog; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface InventoryMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/InventoryService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/InventoryService.java new file mode 100644 index 0000000..2c2d6de --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/InventoryService.java @@ -0,0 +1,16 @@ +package com.pawtrace.server.modules.inventory.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.inventory.entity.Inventory; +import com.pawtrace.server.modules.inventory.entity.InventoryLog; + +import java.util.List; + +public interface InventoryService extends IService { + + /** 当前用户的低库存物品 */ + List listLowStock(Long userId); + + /** 记录一次消耗,自动扣减库存 */ + boolean consume(InventoryLog log); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/impl/InventoryServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/impl/InventoryServiceImpl.java new file mode 100644 index 0000000..d474e48 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/inventory/service/impl/InventoryServiceImpl.java @@ -0,0 +1,46 @@ +package com.pawtrace.server.modules.inventory.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.common.exception.BusinessException; +import com.pawtrace.server.common.constant.ResultCode; +import com.pawtrace.server.modules.inventory.entity.Inventory; +import com.pawtrace.server.modules.inventory.entity.InventoryLog; +import com.pawtrace.server.modules.inventory.mapper.InventoryLogMapper; +import com.pawtrace.server.modules.inventory.mapper.InventoryMapper; +import com.pawtrace.server.modules.inventory.service.InventoryService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.List; + +@Service +public class InventoryServiceImpl extends ServiceImpl implements InventoryService { + + private final InventoryLogMapper logMapper; + + public InventoryServiceImpl(InventoryLogMapper logMapper) { + this.logMapper = logMapper; + } + + @Override + public List listLowStock(Long userId) { + return this.list(new LambdaQueryWrapper() + .eq(Inventory::getUserId, userId) + .and(w -> w.apply("stock <= low_stock_threshold"))); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean consume(InventoryLog log) { + Inventory item = this.getById(log.getInventoryId()); + if (item == null) { + throw new BusinessException(ResultCode.INVENTORY_NOT_FOUND); + } + BigDecimal remain = item.getStock().subtract(log.getQuantity()); + item.setStock(remain); + this.updateById(item); + return logMapper.insert(log) > 0; + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/controller/LifeCardController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/controller/LifeCardController.java new file mode 100644 index 0000000..d0b65bb --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/controller/LifeCardController.java @@ -0,0 +1,39 @@ +package com.pawtrace.server.modules.lifecard.controller; + +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.lifecard.entity.LifeCard; +import com.pawtrace.server.modules.lifecard.service.LifeCardService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 宠物生活信息卡 Controller + * + * @author PawTrace Team + */ +@Tag(name = "宠物生活信息卡") +@RestController +@RequestMapping("/life-card") +public class LifeCardController { + + private final LifeCardService lifeCardService; + + public LifeCardController(LifeCardService lifeCardService) { + this.lifeCardService = lifeCardService; + } + + @Operation(summary = "按分类查询") + @GetMapping("/category/{category}") + public Result> listByCategory(@PathVariable Integer category) { + return Result.success(lifeCardService.listByCategory(category)); + } + + @Operation(summary = "详情") + @GetMapping("/{id}") + public Result detail(@PathVariable Long id) { + return Result.success(lifeCardService.getById(id)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/entity/LifeCard.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/entity/LifeCard.java new file mode 100644 index 0000000..6407e9e --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/entity/LifeCard.java @@ -0,0 +1,55 @@ +package com.pawtrace.server.modules.lifecard.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 宠物生活信息卡(商家/地点) + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_life_card") +@Schema(description = "生活信息卡") +public class LifeCard extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "分类 1医院 2餐厅 3公园 4宠物店 5美容") + private Integer category; + + @Schema(description = "名称") + private String name; + + @Schema(description = "封面图") + private String cover; + + @Schema(description = "地址") + private String address; + + @Schema(description = "联系电话") + private String phone; + + @Schema(description = "简介") + private String description; + + @Schema(description = "经度(腾讯地图GCJ02)") + private BigDecimal longitude; + + @Schema(description = "纬度(腾讯地图GCJ02)") + private BigDecimal latitude; + + @Schema(description = "排序") + private Integer sortOrder; + + @Schema(description = "状态 0下架 1上架") + private Integer status; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/mapper/LifeCardMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/mapper/LifeCardMapper.java new file mode 100644 index 0000000..e03c8dd --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/mapper/LifeCardMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.lifecard.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.lifecard.entity.LifeCard; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface LifeCardMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/LifeCardService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/LifeCardService.java new file mode 100644 index 0000000..6dc8baf --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/LifeCardService.java @@ -0,0 +1,12 @@ +package com.pawtrace.server.modules.lifecard.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.lifecard.entity.LifeCard; + +import java.util.List; + +public interface LifeCardService extends IService { + + /** 按分类查询上架的生活信息卡 */ + List listByCategory(Integer category); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/impl/LifeCardServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/impl/LifeCardServiceImpl.java new file mode 100644 index 0000000..7fd2a3f --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/lifecard/service/impl/LifeCardServiceImpl.java @@ -0,0 +1,27 @@ +package com.pawtrace.server.modules.lifecard.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.common.constant.CommonConstants; +import com.pawtrace.server.modules.lifecard.entity.LifeCard; +import com.pawtrace.server.modules.lifecard.mapper.LifeCardMapper; +import com.pawtrace.server.modules.lifecard.service.LifeCardService; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service +public class LifeCardServiceImpl extends ServiceImpl implements LifeCardService { + + @Override + public List listByCategory(Integer category) { + if (category == null) { + return Collections.emptyList(); + } + return this.list(new LambdaQueryWrapper() + .eq(LifeCard::getCategory, category) + .eq(LifeCard::getStatus, CommonConstants.STATUS_ENABLED) + .orderByAsc(LifeCard::getSortOrder)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/controller/PetController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/controller/PetController.java new file mode 100644 index 0000000..5f3ae64 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/controller/PetController.java @@ -0,0 +1,47 @@ +package com.pawtrace.server.modules.pet.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.pet.entity.Pet; +import com.pawtrace.server.modules.pet.service.PetService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 宠物档案 Controller + * + * @author PawTrace Team + */ +@Tag(name = "宠物档案") +@RestController +@RequestMapping("/pet") +public class PetController { + + private final PetService petService; + + public PetController(PetService petService) { + this.petService = petService; + } + + @Operation(summary = "我的宠物列表") + @GetMapping("/list") + public Result> list() { + return Result.success(petService.listMyPets(StpUtil.getLoginIdAsLong())); + } + + @Operation(summary = "新增/更新宠物") + @PostMapping("/save") + public Result save(@RequestBody Pet pet) { + pet.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(petService.saveOrUpdate(pet)); + } + + @Operation(summary = "删除宠物") + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(petService.removeById(id)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/Pet.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/Pet.java new file mode 100644 index 0000000..496d7b6 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/Pet.java @@ -0,0 +1,55 @@ +package com.pawtrace.server.modules.pet.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 宠物档案 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_pet") +@Schema(description = "宠物档案") +public class Pet extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "所属用户ID") + private Long userId; + + @Schema(description = "昵称") + private String nickname; + + @Schema(description = "品种") + private String breed; + + @Schema(description = "种类 1狗 2猫 3其他") + private Integer species; + + @Schema(description = "性别 0未知 1公 2母") + private Integer gender; + + @Schema(description = "生日") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate birthday; + + @Schema(description = "当前体重(kg)") + private BigDecimal weight; + + @Schema(description = "头像URL") + private String avatar; + + @Schema(description = "性格标签(逗号分隔)") + private String personalityTags; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/PetWeight.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/PetWeight.java new file mode 100644 index 0000000..41e855b --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/entity/PetWeight.java @@ -0,0 +1,43 @@ +package com.pawtrace.server.modules.pet.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 体重历史记录 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_pet_weight") +@Schema(description = "体重记录") +public class PetWeight extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "宠物ID") + private Long petId; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "体重(kg)") + private BigDecimal weight; + + @Schema(description = "记录日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate recordDate; + + @Schema(description = "备注") + private String remark; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetMapper.java new file mode 100644 index 0000000..5f02ab7 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetMapper.java @@ -0,0 +1,15 @@ +package com.pawtrace.server.modules.pet.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.pet.entity.Pet; +import com.pawtrace.server.modules.pet.entity.PetWeight; +import org.apache.ibatis.annotations.Mapper; + +/** + * 宠物 Mapper + * + * @author PawTrace Team + */ +@Mapper +public interface PetMapper extends BaseMapper { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetWeightMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetWeightMapper.java new file mode 100644 index 0000000..4fd6c55 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/mapper/PetWeightMapper.java @@ -0,0 +1,14 @@ +package com.pawtrace.server.modules.pet.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.pet.entity.PetWeight; +import org.apache.ibatis.annotations.Mapper; + +/** + * 体重记录 Mapper + * + * @author PawTrace Team + */ +@Mapper +public interface PetWeightMapper extends BaseMapper { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetService.java new file mode 100644 index 0000000..3ce0b63 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetService.java @@ -0,0 +1,22 @@ +package com.pawtrace.server.modules.pet.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.common.page.PageResult; +import com.pawtrace.server.modules.pet.entity.Pet; +import com.pawtrace.server.modules.pet.entity.PetWeight; + +import java.util.List; + +/** + * 宠物 Service + * + * @author PawTrace Team + */ +public interface PetService extends IService { + + /** 当前用户的宠物列表 */ + List listMyPets(Long userId); + + /** 体重历史分页 */ + PageResult pageWeight(Long userId, Long petId, Long pageNum, Long pageSize); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetWeightService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetWeightService.java new file mode 100644 index 0000000..bf4d27e --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/PetWeightService.java @@ -0,0 +1,12 @@ +package com.pawtrace.server.modules.pet.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.pet.entity.PetWeight; + +/** + * 体重 Service + * + * @author PawTrace Team + */ +public interface PetWeightService extends IService { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetServiceImpl.java new file mode 100644 index 0000000..b22efa9 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetServiceImpl.java @@ -0,0 +1,45 @@ +package com.pawtrace.server.modules.pet.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.common.page.PageResult; +import com.pawtrace.server.modules.pet.entity.Pet; +import com.pawtrace.server.modules.pet.entity.PetWeight; +import com.pawtrace.server.modules.pet.mapper.PetMapper; +import com.pawtrace.server.modules.pet.mapper.PetWeightMapper; +import com.pawtrace.server.modules.pet.service.PetService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 宠物 Service 实现 + * + * @author PawTrace Team + */ +@Service +public class PetServiceImpl extends ServiceImpl implements PetService { + + private final PetWeightMapper petWeightMapper; + + public PetServiceImpl(PetWeightMapper petWeightMapper) { + this.petWeightMapper = petWeightMapper; + } + + @Override + public List listMyPets(Long userId) { + return this.list(new LambdaQueryWrapper().eq(Pet::getUserId, userId)); + } + + @Override + public PageResult pageWeight(Long userId, Long petId, Long pageNum, Long pageSize) { + IPage page = petWeightMapper.selectPage(new Page<>(pageNum, pageSize), + new LambdaQueryWrapper() + .eq(PetWeight::getUserId, userId) + .eq(petId != null, PetWeight::getPetId, petId) + .orderByDesc(PetWeight::getRecordDate)); + return new PageResult<>(page.getTotal(), pageNum, pageSize, page.getRecords()); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetWeightServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetWeightServiceImpl.java new file mode 100644 index 0000000..9f55eda --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/pet/service/impl/PetWeightServiceImpl.java @@ -0,0 +1,16 @@ +package com.pawtrace.server.modules.pet.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.modules.pet.entity.PetWeight; +import com.pawtrace.server.modules.pet.mapper.PetWeightMapper; +import com.pawtrace.server.modules.pet.service.PetWeightService; +import org.springframework.stereotype.Service; + +/** + * 体重 Service 实现 + * + * @author PawTrace Team + */ +@Service +public class PetWeightServiceImpl extends ServiceImpl implements PetWeightService { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/reminder/scheduled/ReminderScheduledTask.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/reminder/scheduled/ReminderScheduledTask.java new file mode 100644 index 0000000..dc36d5d --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/reminder/scheduled/ReminderScheduledTask.java @@ -0,0 +1,67 @@ +package com.pawtrace.server.modules.reminder.scheduled; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.pawtrace.server.modules.health.entity.Vaccine; +import com.pawtrace.server.modules.health.entity.Deworm; +import com.pawtrace.server.modules.health.mapper.VaccineMapper; +import com.pawtrace.server.modules.health.mapper.DewormMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; + +/** + * 提醒扫描任务 + *

+ * 每天 08:00 扫描即将到期的疫苗 / 驱虫,通过微信订阅消息推送给用户。 + * 实际推送实现依赖 wx-java miniapp,此处仅负责检索待推送列表。 + * + * @author PawTrace Team + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ReminderScheduledTask { + + private final VaccineMapper vaccineMapper; + private final DewormMapper dewormMapper; + + @Scheduled(cron = "0 0 8 * * ?") + public void scanUpcomingReminders() { + LocalDate today = LocalDate.now(); + // 1) 即将到期(提前1天) + scanVaccine(today, 1); + scanVaccine(today, 3); + scanVaccine(today, 7); + scanDeworm(today, 1); + scanDeworm(today, 3); + scanDeworm(today, 7); + } + + private void scanVaccine(LocalDate today, int beforeDays) { + LocalDate target = today.plusDays(beforeDays); + List list = vaccineMapper.selectList(Wrappers.lambdaQuery() + .eq(Vaccine::getReminderEnabled, 1) + .eq(Vaccine::getRemindBeforeDays, beforeDays) + .eq(Vaccine::getNextDate, target)); + if (!list.isEmpty()) { + log.info("[提醒] 疫苗 {} 天后到期, 命中 {} 条", beforeDays, list.size()); + // TODO: 调用 wx-java 推送订阅消息 + } + } + + private void scanDeworm(LocalDate today, int beforeDays) { + LocalDate target = today.plusDays(beforeDays); + List list = dewormMapper.selectList(Wrappers.lambdaQuery() + .eq(Deworm::getReminderEnabled, 1) + .eq(Deworm::getRemindBeforeDays, beforeDays) + .eq(Deworm::getNextDate, target)); + if (!list.isEmpty()) { + log.info("[提醒] 驱虫 {} 天后到期, 命中 {} 条", beforeDays, list.size()); + // TODO: 调用 wx-java 推送订阅消息 + } + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/controller/TimeMachineController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/controller/TimeMachineController.java new file mode 100644 index 0000000..22703a0 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/controller/TimeMachineController.java @@ -0,0 +1,54 @@ +package com.pawtrace.server.modules.timemachine.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import com.pawtrace.server.modules.timemachine.entity.TimeMachineWork; +import com.pawtrace.server.modules.timemachine.service.TimeMachineWorkService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 时光机工坊 Controller + * 注:核心处理(对比/滤镜/视频合成)在客户端完成,服务端仅保存作品元数据 + * + * @author PawTrace Team + */ +@Tag(name = "时光机工坊") +@RestController +@RequestMapping("/timemachine") +public class TimeMachineController { + + private final TimeMachineWorkService workService; + + public TimeMachineController(TimeMachineWorkService workService) { + this.workService = workService; + } + + @Operation(summary = "我的作品列表") + @GetMapping("/list") + public Result> list(@RequestParam(required = false) Integer workType, + @RequestParam(required = false) Long petId) { + return Result.success(workService.lambdaQuery() + .eq(TimeMachineWork::getUserId, StpUtil.getLoginIdAsLong()) + .eq(workType != null, TimeMachineWork::getWorkType, workType) + .eq(petId != null, TimeMachineWork::getPetId, petId) + .orderByDesc(TimeMachineWork::getCreateTime) + .list()); + } + + @Operation(summary = "保存作品") + @PostMapping("/save") + public Result save(@RequestBody TimeMachineWork work) { + work.setUserId(StpUtil.getLoginIdAsLong()); + return Result.success(workService.saveOrUpdate(work)); + } + + @Operation(summary = "删除作品") + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(workService.removeById(id)); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/entity/TimeMachineWork.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/entity/TimeMachineWork.java new file mode 100644 index 0000000..e93e636 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/entity/TimeMachineWork.java @@ -0,0 +1,48 @@ +package com.pawtrace.server.modules.timemachine.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 时光机工坊作品 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_timemachine_work") +@Schema(description = "时光机作品") +public class TimeMachineWork extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "作品类型 1同角度对比 2AI滤镜 3时光机视频") + private Integer workType; + + @Schema(description = "关联宠物ID") + private Long petId; + + @Schema(description = "标题") + private String title; + + @Schema(description = "使用的素材URL列表(JSON数组)") + private String sourceUrls; + + @Schema(description = "结果URL(图片/视频)") + private String resultUrl; + + @Schema(description = "模板标识") + private String templateKey; + + @Schema(description = "备注") + private String remark; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/mapper/TimeMachineWorkMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/mapper/TimeMachineWorkMapper.java new file mode 100644 index 0000000..2e77430 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/mapper/TimeMachineWorkMapper.java @@ -0,0 +1,8 @@ +package com.pawtrace.server.modules.timemachine.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.timemachine.entity.TimeMachineWork; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TimeMachineWorkMapper extends BaseMapper {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/TimeMachineWorkService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/TimeMachineWorkService.java new file mode 100644 index 0000000..1d4fb39 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/TimeMachineWorkService.java @@ -0,0 +1,6 @@ +package com.pawtrace.server.modules.timemachine.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.timemachine.entity.TimeMachineWork; + +public interface TimeMachineWorkService extends IService {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/impl/TimeMachineWorkServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/impl/TimeMachineWorkServiceImpl.java new file mode 100644 index 0000000..7f4bdce --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/timemachine/service/impl/TimeMachineWorkServiceImpl.java @@ -0,0 +1,10 @@ +package com.pawtrace.server.modules.timemachine.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.modules.timemachine.entity.TimeMachineWork; +import com.pawtrace.server.modules.timemachine.mapper.TimeMachineWorkMapper; +import com.pawtrace.server.modules.timemachine.service.TimeMachineWorkService; +import org.springframework.stereotype.Service; + +@Service +public class TimeMachineWorkServiceImpl extends ServiceImpl implements TimeMachineWorkService {} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/controller/AuthController.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/controller/AuthController.java new file mode 100644 index 0000000..26293d0 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/controller/AuthController.java @@ -0,0 +1,35 @@ +package com.pawtrace.server.modules.user.controller; + +import cn.dev33.satoken.stp.StpUtil; +import com.pawtrace.server.common.result.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 用户 / 鉴权 Controller + * + * @author PawTrace Team + */ +@Tag(name = "用户认证") +@RestController +@RequestMapping("/auth") +public class AuthController { + + @Operation(summary = "微信小程序登录(占位)") + @GetMapping("/login") + public Result login() { + // TODO: 接入 wx-java miniapp, 校验 code -> openid -> 自动注册 + StpUtil.login(10001L); + return Result.success(StpUtil.getTokenValue()); + } + + @Operation(summary = "退出登录") + @GetMapping("/logout") + public Result logout() { + StpUtil.logout(); + return Result.success(); + } +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/entity/User.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/entity/User.java new file mode 100644 index 0000000..4982a46 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/entity/User.java @@ -0,0 +1,45 @@ +package com.pawtrace.server.modules.user.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.pawtrace.server.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 用户 + * + * @author PawTrace Team + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pt_user") +@Schema(description = "用户") +public class User extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "微信openid") + private String openid; + + @Schema(description = "微信unionid") + private String unionid; + + @Schema(description = "昵称") + private String nickname; + + @Schema(description = "头像URL") + private String avatar; + + @Schema(description = "性别 0未知 1男 2女") + private Integer gender; + + @Schema(description = "手机号") + private String phone; + + @Schema(description = "状态 0禁用 1启用") + private Integer status; +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/mapper/UserMapper.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/mapper/UserMapper.java new file mode 100644 index 0000000..60af788 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/mapper/UserMapper.java @@ -0,0 +1,14 @@ +package com.pawtrace.server.modules.user.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pawtrace.server.modules.user.entity.User; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户 Mapper + * + * @author PawTrace Team + */ +@Mapper +public interface UserMapper extends BaseMapper { +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/UserService.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/UserService.java new file mode 100644 index 0000000..c59f796 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/UserService.java @@ -0,0 +1,17 @@ +package com.pawtrace.server.modules.user.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pawtrace.server.modules.user.entity.User; + +/** + * 用户 Service + * + * @author PawTrace Team + */ +public interface UserService extends IService { + + /** + * 根据 openid 查询用户 + */ + User getByOpenid(String openid); +} diff --git a/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/impl/UserServiceImpl.java b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..6991a28 --- /dev/null +++ b/server-pawtrace/src/main/java/com/pawtrace/server/modules/user/service/impl/UserServiceImpl.java @@ -0,0 +1,22 @@ +package com.pawtrace.server.modules.user.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pawtrace.server.modules.user.entity.User; +import com.pawtrace.server.modules.user.mapper.UserMapper; +import com.pawtrace.server.modules.user.service.UserService; +import org.springframework.stereotype.Service; + +/** + * 用户 Service 实现 + * + * @author PawTrace Team + */ +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Override + public User getByOpenid(String openid) { + return this.getOne(Wrappers.lambdaQuery().eq(User::getOpenid, openid), false); + } +} diff --git a/server-pawtrace/src/main/resources/application-dev.yml b/server-pawtrace/src/main/resources/application-dev.yml new file mode 100644 index 0000000..7364d6b --- /dev/null +++ b/server-pawtrace/src/main/resources/application-dev.yml @@ -0,0 +1,9 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/pawtrace_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + +logging: + level: + com.pawtrace: DEBUG diff --git a/server-pawtrace/src/main/resources/application-prod.yml b/server-pawtrace/src/main/resources/application-prod.yml new file mode 100644 index 0000000..c627efd --- /dev/null +++ b/server-pawtrace/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/pawtrace_prod?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai + username: pawtrace + password: ${DB_PASSWORD} + +logging: + level: + root: WARN + com.pawtrace: INFO diff --git a/server-pawtrace/src/main/resources/application.yml b/server-pawtrace/src/main/resources/application.yml new file mode 100644 index 0000000..cc1cddd --- /dev/null +++ b/server-pawtrace/src/main/resources/application.yml @@ -0,0 +1,125 @@ +server: + port: 8080 + servlet: + context-path: /api + +spring: + application: + name: server-pawtrace + profiles: + active: dev + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + default-property-inclusion: non_null + servlet: + multipart: + max-file-size: 50MB + max-request-size: 100MB + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/pawtrace?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + minimum-idle: 5 + maximum-pool-size: 20 + idle-timeout: 30000 + connection-timeout: 30000 + max-lifetime: 1800000 + data: + redis: + host: localhost + port: 6379 + password: + database: 0 + timeout: 10s + lettuce: + pool: + max-active: 8 + max-wait: -1ms + max-idle: 8 + min-idle: 0 + +mybatis-plus: + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.pawtrace.server.entity + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + cache-enabled: false + global-config: + banner: false + db-config: + id-type: ASSIGN_ID + logic-delete-field: deleted + logic-delete-value: 1 + logic-not-delete-value: 0 + insert-strategy: not_null + update-strategy: not_null + +sa-token: + token-name: Authorization + timeout: 2592000 + active-timeout: -1 + is-concurrent: true + is-share: true + token-style: uuid + is-log: false + is-read-cookie: false + is-read-header: true + +wx: + miniapp: + appid: your-appid-here + secret: your-secret-here + msg-data-format: JSON + +oss: + type: local + local: + path: ./upload/ + domain: http://localhost:8080/api/file/ + qiniu: + access-key: your-access-key + secret-key: your-secret-key + bucket: your-bucket + domain: http://your-domain + aliyun: + endpoint: oss-cn-hangzhou.aliyuncs.com + access-key-id: your-access-key-id + access-key-secret: your-access-key-secret + bucket-name: your-bucket-name + tencent: + secret-id: your-secret-id + secret-key: your-secret-key + region: ap-guangzhou + bucket-name: your-bucket-name + minio: + endpoint: http://localhost:9000 + access-key: minioadmin + secret-key: minioadmin + bucket-name: pawtrace + +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +knife4j: + enable: true + setting: + language: zh_cn + +logging: + level: + root: INFO + com.pawtrace: DEBUG + com.baomidou.mybatisplus: INFO + file: + name: ./logs/server-pawtrace.log + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" diff --git a/server-pawtrace/src/main/resources/sql/pawtrace.sql b/server-pawtrace/src/main/resources/sql/pawtrace.sql new file mode 100644 index 0000000..37aa672 --- /dev/null +++ b/server-pawtrace/src/main/resources/sql/pawtrace.sql @@ -0,0 +1,251 @@ +-- ============================================================ +-- 宠迹 PawTrace 数据库初始化脚本 +-- 数据库: MySQL 8.x +-- 字符集: utf8mb4 / utf8mb4_unicode_ci +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS `pawtrace` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE `pawtrace`; + +-- ------------------------------------------------------------ +-- 1. 用户表 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_user`; +CREATE TABLE `pt_user` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `openid` VARCHAR(64) DEFAULT NULL COMMENT '微信openid', + `unionid` VARCHAR(64) DEFAULT NULL COMMENT '微信unionid', + `nickname` VARCHAR(64) DEFAULT NULL COMMENT '昵称', + `avatar` VARCHAR(255) DEFAULT NULL COMMENT '头像URL', + `gender` TINYINT DEFAULT 0 COMMENT '0未知 1男 2女', + `phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号', + `status` TINYINT DEFAULT 1 COMMENT '0禁用 1启用', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_openid` (`openid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户'; + +-- ------------------------------------------------------------ +-- 2. 宠物档案 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_pet`; +CREATE TABLE `pt_pet` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `nickname` VARCHAR(64) NOT NULL COMMENT '昵称', + `breed` VARCHAR(64) DEFAULT NULL COMMENT '品种', + `species` TINYINT DEFAULT 1 COMMENT '1狗 2猫 3其他', + `gender` TINYINT DEFAULT 0 COMMENT '0未知 1公 2母', + `birthday` DATE DEFAULT NULL, + `weight` DECIMAL(8,2) DEFAULT NULL COMMENT '当前体重kg', + `avatar` VARCHAR(255) DEFAULT NULL, + `personality_tags` VARCHAR(255) DEFAULT NULL COMMENT '逗号分隔', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物档案'; + +-- ------------------------------------------------------------ +-- 3. 体重历史 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_pet_weight`; +CREATE TABLE `pt_pet_weight` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `pet_id` BIGINT NOT NULL, + `weight` DECIMAL(8,2) NOT NULL, + `record_date` DATE NOT NULL, + `remark` VARCHAR(255) DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_pet_id` (`pet_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='体重记录'; + +-- ------------------------------------------------------------ +-- 4. 成长日记 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_diary`; +CREATE TABLE `pt_diary` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `pet_id` BIGINT DEFAULT NULL, + `title` VARCHAR(128) DEFAULT NULL, + `content` TEXT, + `media_urls` JSON DEFAULT NULL COMMENT '图片/视频URL数组', + `mood_tags` VARCHAR(255) DEFAULT NULL, + `location` VARCHAR(255) DEFAULT NULL, + `diary_type` TINYINT DEFAULT 1 COMMENT '1日常 2成长 3医疗 4饮食 5趣事', + `happen_date` DATE DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_user_date` (`user_id`, `happen_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长日记'; + +-- ------------------------------------------------------------ +-- 5. 疫苗 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_vaccine`; +CREATE TABLE `pt_vaccine` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `pet_id` BIGINT NOT NULL, + `vaccine_name` VARCHAR(128) NOT NULL, + `inject_date` DATE DEFAULT NULL, + `next_date` DATE DEFAULT NULL, + `cert_image` VARCHAR(255) DEFAULT NULL, + `reminder_enabled` TINYINT DEFAULT 0, + `remind_before_days` TINYINT DEFAULT 1, + `remark` VARCHAR(255) DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_pet_id` (`pet_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疫苗记录'; + +-- ------------------------------------------------------------ +-- 6. 驱虫 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_deworm`; +CREATE TABLE `pt_deworm` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `pet_id` BIGINT NOT NULL, + `drug_name` VARCHAR(128) NOT NULL, + `drug_type` TINYINT DEFAULT 1 COMMENT '1体内 2体外', + `use_date` DATE DEFAULT NULL, + `next_date` DATE DEFAULT NULL, + `drug_image` VARCHAR(255) DEFAULT NULL, + `reminder_enabled` TINYINT DEFAULT 0, + `remind_before_days` TINYINT DEFAULT 1, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_pet_id` (`pet_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='驱虫记录'; + +-- ------------------------------------------------------------ +-- 7. 就诊记录 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_visit_record`; +CREATE TABLE `pt_visit_record` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `pet_id` BIGINT NOT NULL, + `hospital` VARCHAR(128) DEFAULT NULL, + `reason` VARCHAR(255) DEFAULT NULL, + `diagnosis` TEXT, + `cost` DECIMAL(10,2) DEFAULT NULL, + `visit_date` DATE DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='就诊记录'; + +-- ------------------------------------------------------------ +-- 8. 物资 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_inventory`; +CREATE TABLE `pt_inventory` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `category` TINYINT DEFAULT 6 COMMENT '1粮 2罐头 3零食 4猫砂 5药品 6其他', + `name` VARCHAR(128) NOT NULL, + `stock` DECIMAL(10,2) NOT NULL DEFAULT 0, + `unit` VARCHAR(16) DEFAULT NULL, + `low_stock_threshold` DECIMAL(10,2) DEFAULT 0, + `cover_image` VARCHAR(255) DEFAULT NULL, + `remark` VARCHAR(255) DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物资库存'; + +-- ------------------------------------------------------------ +-- 9. 物资消耗记录 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_inventory_log`; +CREATE TABLE `pt_inventory_log` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `inventory_id` BIGINT NOT NULL, + `user_id` BIGINT NOT NULL, + `quantity` DECIMAL(10,2) NOT NULL, + `remark` VARCHAR(255) DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物资消耗'; + +-- ------------------------------------------------------------ +-- 10. 时光机作品 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_timemachine_work`; +CREATE TABLE `pt_timemachine_work` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `work_type` TINYINT NOT NULL COMMENT '1同角度对比 2AI滤镜 3时光机视频', + `pet_id` BIGINT DEFAULT NULL, + `title` VARCHAR(128) DEFAULT NULL, + `source_urls` JSON DEFAULT NULL, + `result_url` VARCHAR(255) DEFAULT NULL, + `template_key` VARCHAR(64) DEFAULT NULL, + `remark` VARCHAR(255) DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='时光机作品'; + +-- ------------------------------------------------------------ +-- 11. 生活信息卡 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_life_card`; +CREATE TABLE `pt_life_card` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `category` TINYINT NOT NULL COMMENT '1医院 2餐厅 3公园 4宠物店 5美容', + `name` VARCHAR(128) NOT NULL, + `cover` VARCHAR(255) DEFAULT NULL, + `address` VARCHAR(255) DEFAULT NULL, + `phone` VARCHAR(32) DEFAULT NULL, + `description` TEXT, + `longitude` DECIMAL(10,6) DEFAULT NULL, + `latitude` DECIMAL(10,6) DEFAULT NULL, + `sort_order` INT DEFAULT 0, + `status` TINYINT DEFAULT 1 COMMENT '0下架 1上架', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_category` (`category`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生活信息卡'; + +-- ------------------------------------------------------------ +-- 12. 反馈 +-- ------------------------------------------------------------ +DROP TABLE IF EXISTS `pt_feedback`; +CREATE TABLE `pt_feedback` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `fb_type` TINYINT DEFAULT 1, + `content` TEXT NOT NULL, + `contact` VARCHAR(64) DEFAULT NULL, + `images` JSON DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` TINYINT DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='意见反馈'; diff --git a/uniapp-pawtrace/.npmrc b/uniapp-pawtrace/.npmrc new file mode 100644 index 0000000..d8b8386 --- /dev/null +++ b/uniapp-pawtrace/.npmrc @@ -0,0 +1,3 @@ +strict-peer-dependencies=false +auto-install-peers=true +shamefully-hoist=true diff --git a/uniapp-pawtrace/eslint.config.js b/uniapp-pawtrace/eslint.config.js new file mode 100644 index 0000000..9bdf2e1 --- /dev/null +++ b/uniapp-pawtrace/eslint.config.js @@ -0,0 +1,3 @@ +import uniHelper from '@uni-helper/eslint-config' + +export default uniHelper() diff --git a/uniapp-pawtrace/index.html b/uniapp-pawtrace/index.html new file mode 100644 index 0000000..f4960b1 --- /dev/null +++ b/uniapp-pawtrace/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + + +

+ + + diff --git a/uniapp-pawtrace/manifest.config.ts b/uniapp-pawtrace/manifest.config.ts new file mode 100644 index 0000000..0339e47 --- /dev/null +++ b/uniapp-pawtrace/manifest.config.ts @@ -0,0 +1,80 @@ +import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest' + +export default defineManifestConfig({ + 'name': '', + 'appid': '', + 'description': '', + 'versionName': '1.0.0', + 'versionCode': '100', + 'transformPx': false, + /* 5+App特有相关 */ + 'app-plus': { + usingComponents: true, + nvueStyleCompiler: 'uni-app', + compilerVersion: 3, + splashscreen: { + alwaysShowBeforeRender: true, + waiting: true, + autoclose: true, + delay: 0, + }, + /* 模块配置 */ + modules: {}, + /* 应用发布信息 */ + distribute: { + /* android打包配置 */ + android: { + permissions: [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ], + }, + /* ios打包配置 */ + ios: {}, + /* SDK配置 */ + sdkConfigs: {}, + }, + }, + /* 快应用特有相关 */ + 'quickapp': {}, + /* 小程序特有相关 */ + 'mp-weixin': { + appid: '', + setting: { + urlCheck: false, + }, + usingComponents: true, + darkmode: true, + themeLocation: 'theme.json', + }, + 'mp-alipay': { + usingComponents: true, + }, + 'mp-baidu': { + usingComponents: true, + }, + 'mp-toutiao': { + usingComponents: true, + }, + 'h5': { + darkmode: true, + themeLocation: 'theme.json', + }, + 'uniStatistics': { + enable: false, + }, + 'vueVersion': '3', +}) diff --git a/uniapp-pawtrace/package.json b/uniapp-pawtrace/package.json new file mode 100644 index 0000000..ecd5114 --- /dev/null +++ b/uniapp-pawtrace/package.json @@ -0,0 +1,89 @@ +{ + "name": "uniapp-pawtrace", + "type": "module", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "unh dev", + "build": "unh build", + "about": "unh info", + "type-check": "vue-tsc --noEmit", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test": "vitest" + }, + "dependencies": { + "@dcloudio/uni-app": "3.0.0-5000720260410001", + "@dcloudio/uni-app-harmony": "3.0.0-5000720260410001", + "@dcloudio/uni-app-plus": "3.0.0-5000720260410001", + "@dcloudio/uni-components": "3.0.0-5000720260410001", + "@dcloudio/uni-h5": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-alipay": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-baidu": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-harmony": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-jd": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-kuaishou": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-lark": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-qq": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-toutiao": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-weixin": "3.0.0-5000720260410001", + "@dcloudio/uni-mp-xhs": "3.0.0-5000720260410001", + "@dcloudio/uni-quickapp-webview": "3.0.0-5000720260410001", + "@uni-helper/uni-network": "^0.23.1", + "@uni-helper/uni-promises": "^0.2.1", + "@uni-helper/uni-use": "^0.19.17", + "@vueuse/core": "9.13.0", + "echarts": "^6.0.0", + "pinia": "2.2.4", + "uni-echarts": "^2.5.1", + "uview-pro": "^0.5.17", + "vue": "3.4.21", + "vue-i18n": "9.6.2", + "vue-router": "4.5.1", + "z-paging": "^2.8.8" + }, + "devDependencies": { + "@binbinji/vite-plugin-component-placeholder": "^0.0.15", + "@dcloudio/types": "3.4.28", + "@dcloudio/uni-automator": "3.0.0-5000720260410001", + "@dcloudio/uni-cli-shared": "3.0.0-5000720260410001", + "@dcloudio/uni-stacktracey": "3.0.0-5000720260410001", + "@dcloudio/vite-plugin-uni": "3.0.0-5000720260410001", + "@iconify-json/carbon": "^1.2.20", + "@mini-types/alipay": "^3.0.14", + "@types/node": "^25.6.0", + "@uni-helper/eslint-config": "^0.7.1", + "@uni-helper/plugin-uni": "0.1.0", + "@uni-helper/unh": "^0.3.1", + "@uni-helper/uni-types": "^1.0.0-alpha.8", + "@uni-helper/unocss-preset-uni": "^0.2.11", + "@uni-helper/vite-plugin-uni-components": "^0.2.10", + "@uni-helper/vite-plugin-uni-layouts": "^0.1.11", + "@uni-helper/vite-plugin-uni-manifest": "^0.2.12", + "@uni-helper/vite-plugin-uni-pages": "^0.3.24", + "@uni-helper/vite-plugin-uni-platform": "^0.0.5", + "@uni-ku/root": "^1.4.1", + "@vue/runtime-core": "3.4.21", + "@vue/tsconfig": "^0.9.1", + "eslint": "^10.2.1", + "miniprogram-api-typings": "^5.1.2", + "sass": "1.64.2", + "typescript": "5.9.3", + "unocss": "66.0.0", + "vite": "5.2.8", + "vitest": "^4.1.4", + "vitest-environment-uniapp": "^0.0.5", + "vue-tsc": "^3.2.7" + }, + "pnpm": { + "overrides": { + "unconfig": "7.3.2" + } + }, + "overrides": { + "unconfig": "7.3.2" + }, + "resolutions": { + "unconfig": "7.3.2" + } +} \ No newline at end of file diff --git a/uniapp-pawtrace/pages.config.ts b/uniapp-pawtrace/pages.config.ts new file mode 100644 index 0000000..050f645 --- /dev/null +++ b/uniapp-pawtrace/pages.config.ts @@ -0,0 +1,16 @@ +import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages' + +export default defineUniPages({ + pages: [], + globalStyle: { + backgroundColor: '@bgColor', + backgroundColorBottom: '@bgColorBottom', + backgroundColorTop: '@bgColorTop', + backgroundTextStyle: '@bgTxtStyle', + navigationBarBackgroundColor: '#000000', + navigationBarTextStyle: '@navTxtStyle', + navigationBarTitleText: 'Uni Creator', + navigationStyle: 'custom', + }, + subPackages: [], +}) diff --git a/uniapp-pawtrace/src/App.ku.vue b/uniapp-pawtrace/src/App.ku.vue new file mode 100644 index 0000000..553085b --- /dev/null +++ b/uniapp-pawtrace/src/App.ku.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/uniapp-pawtrace/src/App.vue b/uniapp-pawtrace/src/App.vue new file mode 100644 index 0000000..9e80f05 --- /dev/null +++ b/uniapp-pawtrace/src/App.vue @@ -0,0 +1,9 @@ + + + diff --git a/uniapp-pawtrace/src/components/AppFooter.vue b/uniapp-pawtrace/src/components/AppFooter.vue new file mode 100644 index 0000000..483c440 --- /dev/null +++ b/uniapp-pawtrace/src/components/AppFooter.vue @@ -0,0 +1,24 @@ + + + diff --git a/uniapp-pawtrace/src/components/AppLogos.vue b/uniapp-pawtrace/src/components/AppLogos.vue new file mode 100644 index 0000000..b6d11ce --- /dev/null +++ b/uniapp-pawtrace/src/components/AppLogos.vue @@ -0,0 +1,26 @@ + diff --git a/uniapp-pawtrace/src/components/InputEntry.vue b/uniapp-pawtrace/src/components/InputEntry.vue new file mode 100644 index 0000000..6eec9cc --- /dev/null +++ b/uniapp-pawtrace/src/components/InputEntry.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/uniapp-pawtrace/src/main.ts b/uniapp-pawtrace/src/main.ts new file mode 100644 index 0000000..49e7ffa --- /dev/null +++ b/uniapp-pawtrace/src/main.ts @@ -0,0 +1,16 @@ +import { createSSRApp } from 'vue' +import uViewPro from 'uview-pro' + +import 'uno.css' +import * as Pinia from 'pinia' +import App from './App.vue' + +export function createApp() { + const app = createSSRApp(App) + app.use(uViewPro) + app.use(Pinia.createPinia()) + return { + app, + Pinia, + } +} diff --git a/uniapp-pawtrace/src/manifest.json b/uniapp-pawtrace/src/manifest.json new file mode 100644 index 0000000..827f27a --- /dev/null +++ b/uniapp-pawtrace/src/manifest.json @@ -0,0 +1,70 @@ +{ + "name": "", + "appid": "", + "description": "", + "versionName": "1.0.0", + "versionCode": "100", + "transformPx": false, + "uniStatistics": { + "enable": false + }, + "app-plus": { + "usingComponents": true, + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "splashscreen": { + "alwaysShowBeforeRender": true, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "modules": {}, + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + "ios": {}, + "sdkConfigs": {} + } + }, + "h5": { + "darkmode": true, + "themeLocation": "theme.json" + }, + "quickapp": {}, + "mp-weixin": { + "appid": "", + "setting": { + "urlCheck": false + }, + "usingComponents": true, + "darkmode": true, + "themeLocation": "theme.json" + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "vueVersion": "3" +} diff --git a/uniapp-pawtrace/src/pages.json b/uniapp-pawtrace/src/pages.json new file mode 100644 index 0000000..8fc7021 --- /dev/null +++ b/uniapp-pawtrace/src/pages.json @@ -0,0 +1,19 @@ +{ + "pages": [ + { + "path": "pages/index", + "type": "home" + } + ], + "globalStyle": { + "backgroundColor": "@bgColor", + "backgroundColorBottom": "@bgColorBottom", + "backgroundColorTop": "@bgColorTop", + "backgroundTextStyle": "@bgTxtStyle", + "navigationBarBackgroundColor": "#000000", + "navigationBarTextStyle": "@navTxtStyle", + "navigationBarTitleText": "Uni Creator", + "navigationStyle": "custom" + }, + "subPackages": [] +} diff --git a/uniapp-pawtrace/src/pages/index.vue b/uniapp-pawtrace/src/pages/index.vue new file mode 100644 index 0000000..074ffdf --- /dev/null +++ b/uniapp-pawtrace/src/pages/index.vue @@ -0,0 +1,13 @@ + + + diff --git a/uniapp-pawtrace/src/static/github.svg b/uniapp-pawtrace/src/static/github.svg new file mode 100644 index 0000000..94af9fc --- /dev/null +++ b/uniapp-pawtrace/src/static/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/uniapp-pawtrace/src/static/logo.svg b/uniapp-pawtrace/src/static/logo.svg new file mode 100644 index 0000000..21bb93e --- /dev/null +++ b/uniapp-pawtrace/src/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/uniapp-pawtrace/src/static/vite.png b/uniapp-pawtrace/src/static/vite.png new file mode 100644 index 0000000000000000000000000000000000000000..e29faa6d5f4701580a70eec685a1bf13a4ba5ec2 GIT binary patch literal 7685 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 literal 0 HcmV?d00001 diff --git a/uniapp-pawtrace/src/stores/counter.ts b/uniapp-pawtrace/src/stores/counter.ts new file mode 100644 index 0000000..1b6fe03 --- /dev/null +++ b/uniapp-pawtrace/src/stores/counter.ts @@ -0,0 +1,10 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + function increment() { + count.value++ + } + return { count, increment } +}) diff --git a/uniapp-pawtrace/src/theme.json b/uniapp-pawtrace/src/theme.json new file mode 100644 index 0000000..1d9b292 --- /dev/null +++ b/uniapp-pawtrace/src/theme.json @@ -0,0 +1,26 @@ +{ + "light": { + "bgColor": "#fcfcfc", + "bgColorBottom": "#fcfcfc", + "bgColorTop": "#ff6b00", + "bgTxtStyle": "dark", + "navBgColor": "#ff6b00", + "navTxtStyle": "white", + "tabBgColor": "#fcfcfc", + "tabBorderStyle": "black", + "tabFontColor": "#1f2937", + "tabSelectedColor": "#ff6b00" + }, + "dark": { + "bgColor": "#181818", + "bgColorBottom": "#181818", + "bgColorTop": "#ff6b00", + "bgTxtStyle": "light", + "navBgColor": "#ff6b00", + "navTxtStyle": "white", + "tabBgColor": "#181818", + "tabBorderStyle": "white", + "tabFontColor": "#f3f4f6", + "tabSelectedColor": "#ff6b00" + } +} diff --git a/uniapp-pawtrace/src/uni.scss b/uniapp-pawtrace/src/uni.scss new file mode 100644 index 0000000..3c8e349 --- /dev/null +++ b/uniapp-pawtrace/src/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color: #333; // 基本色 +$uni-text-color-inverse: #fff; // 反色 +$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable: #c0c0c0; + +/* 背景颜色 */ +$uni-bg-color: #fff; +$uni-bg-color-grey: #f8f8f8; +$uni-bg-color-hover: #f1f1f1; // 点击状态颜色 +$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色 + +/* 边框颜色 */ +$uni-border-color: #c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm: 12px; +$uni-font-size-base: 14px; +$uni-font-size-lg: 16px; + +/* 图片尺寸 */ +$uni-img-size-sm: 20px; +$uni-img-size-base: 26px; +$uni-img-size-lg: 40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2c405a; // 文章标题颜色 +$uni-font-size-title: 20px; +$uni-color-subtitle: #555; // 二级标题颜色 +$uni-font-size-subtitle: 18px; +$uni-color-paragraph: #3f536e; // 文章段落颜色 +$uni-font-size-paragraph: 15px; diff --git a/uniapp-pawtrace/test/index.test.ts b/uniapp-pawtrace/test/index.test.ts new file mode 100644 index 0000000..bd975e5 --- /dev/null +++ b/uniapp-pawtrace/test/index.test.ts @@ -0,0 +1,15 @@ +import { beforeAll, describe, expect, it } from 'vitest' + +describe('test title', () => { + let page: Page + beforeAll(async () => { + page = await program.currentPage() + await page.waitFor(3000) + }) + + it('check uni-helper logo label', async () => { + const el = await page.$('.uni-helper-logo__label') + const titleText = await el.text() + expect(titleText).toEqual('uni-helper') + }) +}) diff --git a/uniapp-pawtrace/tsconfig.json b/uniapp-pawtrace/tsconfig.json new file mode 100644 index 0000000..d43de6f --- /dev/null +++ b/uniapp-pawtrace/tsconfig.json @@ -0,0 +1,49 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + "types": [ + "vite/client", + "@dcloudio/types", + "@mini-types/alipay", + "miniprogram-api-typings", + "@uni-helper/uni-types", + "@uni-helper/vite-plugin-uni-pages", + "vitest-environment-uniapp/types", + "uni-echarts/global", + "z-paging/types", + "uview-pro/types" + ], + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "jsx": "preserve", + "jsxImportSource": "vue", + "noImplicitThis": true, + "strict": true, + "verbatimModuleSyntax": true, + "target": "ESNext", + "useDefineForClassFields": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + }, + "vueCompilerOptions": { + "plugins": [ + "@uni-helper/uni-types/volar-plugin" + ] + }, + "includes": [ + "src/**/*.ts", + "src/**/*.tsx" + ] +} diff --git a/uniapp-pawtrace/unh.config.ts b/uniapp-pawtrace/unh.config.ts new file mode 100644 index 0000000..b503604 --- /dev/null +++ b/uniapp-pawtrace/unh.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from '@uni-helper/unh' + +/** + * unh 配置文件 + * 更多配置请参考:https://uni-helper.js.org/unh/ + */ +export default defineConfig({ + platform: { + // 默认平台 + default: 'h5', + // 平台别名 + alias: { + 'h5': ['w', 'h'], + 'mp-weixin': 'wx', + }, + }, + autoGenerate: { + pages: true, + manifest: true, + }, +}) diff --git a/uniapp-pawtrace/uno.config.ts b/uniapp-pawtrace/uno.config.ts new file mode 100644 index 0000000..4fe9547 --- /dev/null +++ b/uniapp-pawtrace/uno.config.ts @@ -0,0 +1,26 @@ +import { + defineConfig, + presetIcons, + transformerDirectives, + transformerVariantGroup, +} from 'unocss' + +import { presetUni } from '@uni-helper/unocss-preset-uni' + +export default defineConfig({ + presets: [ + presetUni(), + presetIcons({ + scale: 1.2, + warn: true, + extraProperties: { + 'display': 'inline-block', + 'vertical-align': 'middle', + }, + }), + ], + transformers: [ + transformerDirectives(), + transformerVariantGroup(), + ], +}) diff --git a/uniapp-pawtrace/vite.config.ts b/uniapp-pawtrace/vite.config.ts new file mode 100644 index 0000000..4351af8 --- /dev/null +++ b/uniapp-pawtrace/vite.config.ts @@ -0,0 +1,67 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import Components from '@uni-helper/vite-plugin-uni-components' +import { uViewProResolver } from '@uni-helper/vite-plugin-uni-components/resolvers' +import { ZPagingResolver } from '@uni-helper/vite-plugin-uni-components/resolvers' +import { UniEchartsResolver } from 'uni-echarts/resolver' +import UniPages from '@uni-helper/vite-plugin-uni-pages' +import UniLayouts from '@uni-helper/vite-plugin-uni-layouts' +import UniManifest from '@uni-helper/vite-plugin-uni-manifest' +import UniPlatform from '@uni-helper/vite-plugin-uni-platform' +import UniRoot from '@uni-ku/root' +import ComponentPlaceholder from '@binbinji/vite-plugin-component-placeholder' +import { UniEcharts } from 'uni-echarts/vite' +import Uni from '@uni-helper/plugin-uni' +import UnoCSS from 'unocss/vite' + +export default defineConfig({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + }, + plugins: [ + // https://uni-helper.js.org/vite-plugin-uni-components + Components({ + dts: true, + resolvers: [UniEchartsResolver(), ZPagingResolver(), uViewProResolver()] + }), + // https://uni-helper.js.org/vite-plugin-uni-pages + UniPages(), + // https://uni-helper.js.org/vite-plugin-uni-layouts + UniLayouts(), + // https://uni-helper.js.org/vite-plugin-uni-manifest + UniManifest(), + // https://uni-helper.js.org/vite-plugin-uni-platform + UniPlatform(), + // https://github.com/uni-ku/root + UniRoot(), + // https://github.com/chouchouji/vite-plugin-component-placeholder + ComponentPlaceholder(), + // https://uni-echarts.xiaohe.ink + UniEcharts(), + // https://uni-helper.js.org/plugin-uni + Uni(), + UnoCSS(), + ], + build: { + target: "es6", + cssTarget: "chrome61" + }, + optimizeDeps: { + exclude: [ + "vue-demi", + "uni-echarts" + ] + }, + css: { + preprocessorOptions: { + scss: { + additionalData: "@import \"uview-pro/theme.scss\";" + } + } + } +}) + + diff --git a/uniapp-pawtrace/vitest.config.ts b/uniapp-pawtrace/vitest.config.ts new file mode 100644 index 0000000..6ec9830 --- /dev/null +++ b/uniapp-pawtrace/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'uniapp', + environmentOptions: { + uniapp: { + platform: 'mp-weixin', + projectPath: './src', + port: 5121, + }, + }, + }, +})