Compare commits

...

165 Commits

Author SHA1 Message Date
Agent
ed0138c777 feat: 新增获取所有商品接口(包括下架)用于商品管理
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-01 15:18:41 +00:00
Agent
0a62e19d51 feat: 订单明细增加长度、宽度、面积字段 2026-04-01 14:01:55 +00:00
Agent
083a8cb4c7 fix: 创建V12安全处理订单明细外键约束
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-01 00:42:41 +00:00
Agent
6446f75239 fix: 移除订单明细的级联删除,商品信息已冗余
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:30:15 +00:00
Agent
abd1d32e14 feat: 订单明细增加商品冗余字段,保存完整商品信息
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:27:04 +00:00
Agent
161fe60bd7 clean: 删除V8中无效的deleted SQL
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:09:13 +00:00
Agent
3f7f6f8c32 fix: 新增V9迁移,商品表外键添加级联删除
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:05:31 +00:00
Agent
5750adeb03 fix: 删除分类检查是否有商品使用
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:03:36 +00:00
Agent
18832cf72d fix: 删除分类改为直接删除
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 16:01:47 +00:00
Agent
e39643fdb7 fix: 删除商品改为硬删除,删除分类需检查是否有商品
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 15:57:22 +00:00
Agent
5d9774aaaf fix: 删除分类时先置空关联产品的category_id
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 15:49:27 +00:00
Agent
6b9ecb43a2 fix: V8迁移添加硬删除产品和置空category_id
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 15:44:13 +00:00
Agent
7fbb95542a 删除种类属性相关代码,回滚V7并创建V8
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-31 13:44:57 +00:00
Agent
75e996aa30 feat: 支持公式属性自动计算(如面积=长*宽)
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-30 13:45:07 +00:00
Agent
35a56fe17d fix: 修复ProductController中saveCategoryAttributes参数类型为CategoryAttribute
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-30 13:34:13 +00:00
Agent
6ee37775b1 fix: 恢复实体类型并处理Boolean转Integer问题
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-30 13:28:08 +00:00
Agent
c4de5572f5 fix: 补回Map import
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-30 13:24:32 +00:00
Agent
4611bfed18 fix: 修复种类属性保存时List无泛型导致MyBatis报错
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-30 13:22:06 +00:00
Agent
c3d764ec40 fix: 增加日志排查保存属性问题
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 15:22:44 +00:00
Agent
a51427790b fix: 修复保存种类属性数据接收问题
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 15:20:35 +00:00
Agent
01afb31fdc fix: 恢复V6原内容匹配数据库checksum
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 15:04:29 +00:00
Agent
842e09aac5 fix: 恢复V6迁移文件(空文件)解决Flyway校验失败
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 15:00:43 +00:00
Agent
fe60e7647b fix: 修复ServiceImpl编译错误,改用普通实现类
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 14:53:33 +00:00
Agent
ac599762da feat: 种类属性管理,支持定义属性模板和商品属性值
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-29 14:45:26 +00:00
Agent
9a51e16fef fix: 必须传入customerId校验
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 07:28:41 +00:00
Agent
19d7917b39 fix: 公开订单接口支持不传customerId(兼容旧订单)
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 07:25:12 +00:00
Agent
4f5fbcdc8f fix: 公开订单接口增加customerId校验,防止盗查
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 07:18:26 +00:00
Agent
b422efe757 feat: 新增公开订单查看接口,无需认证
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 07:09:54 +00:00
Agent
ae47dde897 revert: 回滚打印机模块(云服务器无法访问局域网打印机)
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 06:45:45 +00:00
Agent
4dddae325c feat: 添加打印机管理模块,支持局域网打印
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 06:38:27 +00:00
Agent
de0a9c1fa7 feat: 增加客户种类字段migration
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 05:52:25 +00:00
Agent
02383f1f3a feat: 客户支持type字段,按种类过滤
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-29 05:44:42 +00:00
69365e076e 更新 .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-28 15:15:25 +00:00
Agent
3e19abd320 fix: OrderService接口增加customerName参数 2026-03-28 15:13:39 +00:00
Agent
74666a33cb feat: 后端支持按客户姓名查询订单
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-28 03:25:40 +00:00
Agent
1888d82f58 fix: 后端更新订单时处理discountMoney字段,修复status为null的问题
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-28 03:10:44 +00:00
Agent
8e9e614521 fix: 修复status为null时的空指针异常
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-28 02:20:01 +00:00
Agent
f672b5dacc fix: 移除status的@TableLogic注解,只使用deleted字段
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-28 02:15:50 +00:00
230d7d451e 更新 .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-28 02:09:11 +00:00
Agent
2ac5483eb2 fix: 创建订单时设置deleted=0 2026-03-28 02:05:08 +00:00
Agent
8ae9200707 fix: 修复逻辑删除字段冲突,status用于订单状态,deleted用于删除标记
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-27 16:15:33 +00:00
Agent
289f3ce2ad fix: 修复优惠金额判断逻辑,>=0时使用
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-27 15:33:35 +00:00
Agent
4bbafb3de2 fix: Flyway使用已知可用版本8.5.13
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-27 14:53:28 +00:00
Agent
0e2d60395d fix: 移除Flyway手动版本号,使用Spring Boot parent管理的版本
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-27 14:49:24 +00:00
Agent
f0a5034b9a fix: 补充Flyway版本号
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-27 14:46:43 +00:00
Agent
f04754c325 feat: 集成Flyway数据库版本管理,迁移SQL文件到db/migration目录
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-27 14:40:44 +00:00
Agent
da2da5fe75 fix: 订单表增加discount_money字段
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-27 14:02:59 +00:00
Agent
0fe6f473df feat: 订单支持优惠金额字段,优先使用优惠金额
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-27 13:45:22 +00:00
Agent
2f8a34e2e0 fix: data.sql插入前先删除数据
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-25 16:10:06 +00:00
45199d8aba 更新 src/main/resources/application.yml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-25 16:03:58 +00:00
Agent
96ee3da7d0 feat: 订单状态0未完成+编辑+确认完成功能
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-25 16:00:44 +00:00
a3beef7d29 更新 src/main/resources/application.yml
Some checks are pending
continuous-integration/drone/push Build is running
2026-03-25 14:32:27 +00:00
Agent
aad2ea0904 fix: 修复订单统计日期参数类型转换
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-25 00:56:01 +00:00
77779cfdbd 更新 .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 15:36:12 +00:00
Agent
0a474babea feat: 添加测试数据data.sql,启动时自动加载 2026-03-24 15:31:39 +00:00
029745021c 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 15:16:31 +00:00
cc191c7f03 更新 src/main/resources/application.yml 2026-03-24 15:04:48 +00:00
4766a599b8 更新 src/main/resources/sql/init.sql
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 15:00:48 +00:00
Agent
9e2ddf56fc fix: 升级PostgreSQL驱动到42.7.1支持16 2026-03-24 14:56:40 +00:00
ac111fbc74 更新 .drone.yml 2026-03-24 14:44:45 +00:00
cc5f37fbe2 更新 k8s/secret.yaml 2026-03-24 14:43:00 +00:00
d7a476f7b4 删除 src/main/java/com/example/building/config/MybatisPlusMetaObjectHandler.java
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:28:30 +00:00
Agent
be908f7534 fix: 简化sql.init配置
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:20:41 +00:00
Agent
8435048156 fix: 使用Spring Boot原生sql.init自动建表
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:19:08 +00:00
Agent
e09440ba63 feat: 添加启动时自动建表功能
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:15:51 +00:00
Agent
8683ef126b feat: 添加SQL初始化文件和自动填充处理器 2026-03-24 14:07:59 +00:00
46ca9c7931 更新 k8s/secret.yaml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:03:40 +00:00
d9218793cf 更新 k8s/configmap.yaml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 14:02:02 +00:00
Agent
7b760f794b feat: 添加ConfigMap管理配置,密码使用Secret 2026-03-24 13:45:14 +00:00
Agent
a8b8d270ab fix: 添加spring-boot-maven-plugin打包可执行JAR
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 13:26:09 +00:00
8c5295094b 更新 .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 13:12:01 +00:00
befbb692a9 更新 .drone.yml 2026-03-24 13:10:08 +00:00
Agent
d03bd5a36c fix: 修复AuthServiceImpl重复声明customerMapper
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-24 13:00:10 +00:00
Agent
be26d87552 fix: 修复Customer重复字段和OrderController错误导入
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:53:22 +00:00
72ffc10f6c 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:28:43 +00:00
1196328ddb 更新 pom.xml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:27:58 +00:00
3aaf217287 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:24:54 +00:00
01d3ef2abf 更新 pom.xml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:22:58 +00:00
b2e315a1de 删除 maven-cache-pvc.yaml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 12:17:55 +00:00
Agent
614570a244 fix: 统一Lombok版本解决编译问题 2026-03-24 12:11:56 +00:00
Agent
4368fded04 fix: 手动添加getter/setter解决Lombok编译问题 2026-03-24 12:10:07 +00:00
Agent
cffb88d368 feat: 微信登录获取用户信息,客户列表按最后登录排序 2026-03-24 03:24:10 +00:00
Agent
4fb18fc40e feat: 微信登录获取用户信息,客户列表按最后登录排序,修复Lombok配置
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 03:17:27 +00:00
Agent
f0ce1fe825 fix: 改用 kubectl set image 避免权限问题
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 01:06:05 +00:00
Agent
4c163620be fix: 修复 sed 命令语法
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 01:03:07 +00:00
Agent
709fb0433b fix: 修复 sed 权限问题
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 00:59:50 +00:00
Agent
8c1b5d8be3 feat: 商品维护接口仅管理员可操作
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-24 00:55:43 +00:00
Agent
3d84686fcf feat: 添加顾客权限控制和订单时间限制 2026-03-24 00:40:15 +00:00
Agent
8e47a764b3 fix: dev 自动替换镜像 tag 并重启
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 15:57:33 +00:00
Agent
9567309655 fix: dev 模式始终拉取最新镜像
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-23 15:41:56 +00:00
Agent
a03c097dfb fix: 添加 imagePullSecrets 拉取镜像
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-23 13:18:03 +00:00
Agent
1368b13488 add: 添加 k8s deployment.yaml,dev 用 apply 2026-03-23 13:13:14 +00:00
8d1d677a33 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 13:00:51 +00:00
0c43adc6a8 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:52:01 +00:00
Agent
e5df00b806 chore: 使用 in-cluster 模式,添加 cluster 和 namespace 2026-03-23 12:36:41 +00:00
Agent
6834d7c83c fix: 简化 Dockerfile,Drone CI 已构建 jar 包
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:23:22 +00:00
Agent
7b9a858fbc chore: 改用 repo+tags 格式
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:20:08 +00:00
Agent
9a663f80a8 chore: 添加 username 和 password
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:18:54 +00:00
Agent
eca597f3e4 chore: 添加 registry 参数解决 Docker Hub 认证问题
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:17:36 +00:00
Agent
69609352ba chore: 简化 kaniko 配置,使用 destination
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 12:12:14 +00:00
14fcb55e51 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-23 11:57:54 +00:00
559db4c1e1 更新 .drone.yml 2026-03-23 11:53:38 +00:00
Agent
30baa1a00a chore: 按照官方 kaniko 插件格式配置 2026-03-23 11:50:12 +00:00
Agent
8d286b6d2f chore: 使用 drone-kaniko 替代 kaniko-project-executor 2026-03-23 11:47:16 +00:00
Agent
cb0a8615df fix: use settings instead of commands for kaniko
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 17:28:58 +00:00
6f917c8f88 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 17:18:02 +00:00
6a187dc53f 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 17:16:22 +00:00
089ff04ff6 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 17:13:03 +00:00
06c58e3333 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 17:04:43 +00:00
30a9ad15c7 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 16:59:58 +00:00
678135dc80 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 16:43:41 +00:00
507f64fac6 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-22 16:42:21 +00:00
e0f5523a11 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 16:38:40 +00:00
415b16fb17 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 16:34:43 +00:00
f2b65cc219 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 16:27:15 +00:00
Agent
01778fbfef debug: pause dev-build for inspection
Some checks failed
continuous-integration/drone/push Build was killed
2026-03-22 15:44:43 +00:00
Agent
d35d19c30a debug: pause dev-build-image for inspection
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 15:41:40 +00:00
Agent
2dd4a3dedf debug: add verbosity and ls for kaniko
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 15:39:06 +00:00
Agent
6103847d4e fix: correct PVC volume syntax
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 08:44:37 +00:00
1da24de6d1 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 08:39:08 +00:00
Agent
697dc03d59 restore: use PVC for maven cache
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 06:01:21 +00:00
Agent
2cf6e108ca temp: use emptyDir to test pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 05:37:11 +00:00
Agent
81fe1c6adb restore: use PVC for maven cache
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 05:34:56 +00:00
Agent
b19f09af24 simplify: share workspace between steps
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 05:33:16 +00:00
Agent
b7268f4447 test: use emptyDir for maven cache
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 05:22:13 +00:00
Agent
01fd4b90ce fix: share source code between steps using volume
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 05:12:02 +00:00
Agent
ac2c95ae69 feat: add maven cache using PVC
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 04:45:11 +00:00
Agent
d1e39c2179 fix: use docker_config secret for dev-build-image
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 04:29:15 +00:00
0025d9d6e7 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 04:16:37 +00:00
Agent
48512a4027 fix: use kaniko instead of docker
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 03:56:55 +00:00
606e1402f2 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 03:18:58 +00:00
Agent
df3af2ade4 fix: use Java 17 in pom.xml 2026-03-22 03:15:00 +00:00
Agent
2af7db1fe8 fix: use Java 17 instead of 22 2026-03-22 03:08:13 +00:00
Agent
73c2377afd fix: Jackson2JsonRedisSerializer constructor
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 02:57:58 +00:00
Agent
1aa1eee372 fix: add missing NotBlank import
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 02:05:30 +00:00
Agent
e6cc7ff683 fix: use current directory for default clone
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-22 01:56:15 +00:00
cd0807f1fc 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-22 01:51:59 +00:00
47ce88eb2b 更新 .drone.yml
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-22 01:51:14 +00:00
Agent
e2eb0a9ce1 fix: use public clone without credentials
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-21 15:50:24 +00:00
Agent
038c10699a Revert "chore: use default clone for public repo"
Some checks failed
continuous-integration/drone/push Build is failing
This reverts commit 176f587996.
2026-03-21 15:49:49 +00:00
Agent
176f587996 chore: use default clone for public repo
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 15:47:38 +00:00
Agent
3a5046809b fix: use custom clone step instead of default
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 15:30:17 +00:00
Agent
888fab6805 fix: use disable instead of disabled
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 14:34:40 +00:00
Agent
97928ddeda fix: correct clone indentation
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 14:31:29 +00:00
Agent
b28df6b2af fix: disable default clone
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-21 14:30:05 +00:00
Agent
d4a0af5f80 fix: add unique step names per pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 14:27:26 +00:00
Agent
d1f218d170 chore: use custom git-alpine image
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-21 14:24:12 +00:00
Agent
d57df76616 fix: add clone step with git credentials
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-21 14:13:38 +00:00
Agent
9be32342d3 Clean config
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-21 02:10:12 +00:00
Agent
13fc39d484 Use drone namespace
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-21 01:26:26 +00:00
Agent
a8bfaf2d4b Add git credentials 2026-03-20 16:37:54 +00:00
Agent
7616fa5e7f Simplify drone config
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-20 16:25:03 +00:00
Agent
8e0bd4ac6c Use private kubectl image
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 16:17:42 +00:00
Agent
139f49e87b Revert to private registry 2026-03-20 15:50:35 +00:00
Agent
488dd0ae56 Fix image names 2026-03-20 15:47:39 +00:00
Agent
cbec4824de Fix Drone variable syntax to ${DRONE_COMMIT_SHA}
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:34:49 +00:00
Agent
209bc304ba Use violin namespace for images
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:32:19 +00:00
Agent
a9139f0388 Fix Drone variable: DRONE_COMMIT_SHA 2026-03-20 15:27:36 +00:00
Agent
d1e66c8101 Change pipeline type to kubernetes 2026-03-20 15:18:18 +00:00
Agent
45435b7601 Java version 22
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:14:28 +00:00
Agent
ff71aa633c Config via environment variables
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:13:07 +00:00
Agent
ce92e1e056 Fix: use correct image tags (no alpine)
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:08:52 +00:00
Agent
7b7ad367ed Update base image to ccr.ccs.tencentyun.com/violin/eclipse-temurin:22
Some checks failed
continuous-integration/drone/push Build encountered an error
2026-03-20 15:04:01 +00:00
Agent
141f890d1a Add K8s deployment in CI/CD 2026-03-20 14:28:42 +00:00
Agent
e4fa8ff7ad Add .drone.yml for CI/CD 2026-03-20 14:23:28 +00:00
42 changed files with 1266 additions and 107 deletions

97
.drone.yml Normal file
View File

@@ -0,0 +1,97 @@
kind: pipeline
type: kubernetes
name: build-and-deploy-dev
volumes:
- name: maven-cache
claim:
name: maven-cache-pvc
service_account_name: drone-builder-sa
trigger:
event:
- push
branch:
- dev
steps:
- name: dev-build
image: ccr.ccs.tencentyun.com/violin/maven:3.9-eclipse-temurin-22
commands:
- mvn clean package -DskipTests
volumes:
- name: maven-cache
path: /root/.m2
- name: dev-build-image
# 使用 plugin/kaniko # # # # # #
image: ccr.ccs.tencentyun.com/violin/drone-kaniko:latest
settings:
repo: ccr.ccs.tencentyun.com/violin/todo-backend
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
dockerfile: Dockerfile
context: .
registry: ccr.ccs.tencentyun.com
username: 100024540033
password:
from_secret: docker_password
- name: dev-deploy
image: ccr.ccs.tencentyun.com/violin/kubectl:latest
cluster: kubernetes
namespace: drone
commands:
- kubectl set image deployment/todo-backend todo-backend=ccr.ccs.tencentyun.com/violin/todo-backend:${DRONE_COMMIT_SHA:0:8} -n drone
- kubectl rollout restart deployment/todo-backend -n drone
---
kind: pipeline
type: kubernetes
name: build-and-deploy-prod
volumes:
- name: maven-cache
claim:
name: maven-cache-pvc
trigger:
event:
- push
branch:
- master
steps:
- name: prod-build
image: ccr.ccs.tencentyun.com/violin/maven:3.9-eclipse-temurin-22
commands:
- mvn clean package -DskipTests
volumes:
- name: maven-cache
path: /root/.m2
- name: prod-build-image
##
image: ccr.ccs.tencentyun.com/violin/drone-kaniko:latest
settings:
repo: ccr.ccs.tencentyun.com/violin/todo-backend
tags:
- v1.0.0
dockerfile: Dockerfile
context: .
registry: ccr.ccs.tencentyun.com
username: 100024540033
password:
from_secret: docker_password
- name: prod-deploy
image: ccr.ccs.tencentyun.com/violin/kubectl:latest
cluster: kubernetes
namespace: drone
commands:
- kubectl set image deployment/todo-backend todo-backend=ccr.ccs.tencentyun.com/violin/todo-backend:v1.0.0
- kubectl rollout status deployment/todo-backend

View File

@@ -1,18 +1,8 @@
FROM eclipse-temurin:17-jdk-alpine AS builder
FROM ccr.ccs.tencentyun.com/violin/eclipse-temurin:22-jre
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN apk add --no-cache maven && \
mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
COPY target/*.jar app.jar
EXPOSE 8080

16
k8s/configmap.yaml Normal file
View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: todo-backend-config
namespace: drone
data:
SPRING_DATASOURCE_HOST: "192.168.3.49"
SPRING_DATASOURCE_PORT: "5432"
SPRING_DATASOURCE_DB: "sales_dev"
SPRING_DATASOURCE_USERNAME: "sales_dev"
SPRING_REDIS_HOST: "redis.drone.svc.cluster.local"
SPRING_REDIS_PORT: "6379"
SPRING_REDIS_PASSWORD: ""
JWT_SECRET: "building-materials-secret-key-2024"
JWT_EXPIRATION: "7200000"
JWT_REFRESH_EXPIRATION: "604800000"

47
k8s/deployment.yaml Normal file
View File

@@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-backend
namespace: drone
labels:
app: todo-backend
spec:
replicas: 1
selector:
matchLabels:
app: todo-backend
template:
metadata:
labels:
app: todo-backend
spec:
imagePullSecrets:
- name: tencentyun-secret
containers:
- name: todo-backend
image: ccr.ccs.tencentyun.com/violin/todo-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: todo-backend-config
env:
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: todo-backend-secret
key: SPRING_DATASOURCE_PASSWORD
---
apiVersion: v1
kind: Service
metadata:
name: todo-backend
namespace: drone
spec:
selector:
app: todo-backend
ports:
- port: 80
targetPort: 8080
type: ClusterIP

9
k8s/secret.yaml Normal file
View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: todo-backend-secret
namespace: drone
type: Opaque
data:
# echo -n 'your_password' | base64
SPRING_DATASOURCE_PASSWORD: c2FsZXNfZGV2Cg==

39
pom.xml
View File

@@ -46,6 +46,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.1</version>
<scope>runtime</scope>
</dependency>
@@ -79,7 +80,8 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- Apache Commons -->
@@ -95,6 +97,13 @@
<version>2.0.43</version>
</dependency>
<!-- Flyway 数据库版本管理 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>8.5.13</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -106,17 +115,33 @@
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<excludes>
<exclude>
<release>17</release>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
<version>1.18.30</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -30,7 +30,8 @@ public class RedisConfig {
ObjectMapper.DefaultTyping.NON_FINAL);
// 使用Jackson2JsonRedisSerializer序列化value
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(mapper, Object.class);
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
serializer.setObjectMapper(mapper);
// 设置key和value的序列化方式
template.setKeySerializer(new StringRedisSerializer());

View File

@@ -38,10 +38,16 @@ public class AuthController {
/**
* 微信登录
* @param code 微信授权码
* @param nickname 微信昵称(可选)
* @param avatar 微信头像URL可选
*/
@PostMapping("/wechat")
public Result<Map<String, Object>> wechatLogin(@RequestParam String code) {
Map<String, Object> result = authService.wechatLogin(code);
public Result<Map<String, Object>> wechatLogin(
@RequestParam String code,
@RequestParam(required = false) String nickname,
@RequestParam(required = false) String avatar) {
Map<String, Object> result = authService.wechatLogin(code, nickname, avatar);
return Result.success(result);
}

View File

@@ -23,9 +23,10 @@ public class CustomerController {
@GetMapping
public Result<Page<Customer>> getCustomers(
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String type,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer pageSize) {
return Result.success(customerService.getCustomers(keyword, page, pageSize));
return Result.success(customerService.getCustomers(keyword, type, page, pageSize));
}
/**

View File

@@ -5,6 +5,7 @@ import com.example.building.common.Result;
import com.example.building.dto.CreateOrderRequest;
import com.example.building.entity.Order;
import com.example.building.service.OrderService;
import com.example.building.service.SystemConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -16,6 +17,7 @@ import java.util.Map;
* - 订单创建:计算原价(total_amount)、优惠金额(discount_amount)、实付金额(actual_amount)
* - 订单原价 = 商品标价 × 数量之和
* - 实付金额 = 原价 - 优惠金额
* - 顾客角色:只能查看半年内的订单(可配置)
*/
@RestController
@RequestMapping("/api/v1/orders")
@@ -24,40 +26,57 @@ public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private SystemConfigService systemConfigService;
/**
* 创建订单
* 核心逻辑:
* 1. 计算订单原价(total_amount) = Σ(item.price × item.quantity)
* 2. 计算优惠金额(discount_amount) = total_amount × (100 - discount_rate) / 100
* 3. 计算实付金额(actual_amount) = total_amount - discount_amount
* 顾客角色不允许创建订单
*/
@PostMapping
public Result<Order> createOrder(@RequestBody CreateOrderRequest request,
@RequestHeader("X-User-Id") String operatorId,
@RequestHeader("X-Username") String operatorName) {
@RequestHeader("X-Username") String operatorName,
@RequestHeader(value = "X-User-Role", required = false) String role) {
// 顾客角色不允许创建订单
if ("customer".equals(role)) {
return Result.error("顾客账号不允许创建订单,请联系销售人员");
}
return Result.success(orderService.createOrder(request, operatorId, operatorName));
}
/**
* 获取订单列表
* 顾客角色只能看到半年内的订单(可配置)
*/
@GetMapping
public Result<Page<Order>> getOrders(
@RequestParam(required = false) String customerId,
@RequestParam(required = false) String customerName,
@RequestParam(required = false) Integer status,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer pageSize) {
return Result.success(orderService.getOrders(customerId, status, startDate, endDate, page, pageSize));
@RequestParam(defaultValue = "20") Integer pageSize,
@RequestHeader(value = "X-User-Role", required = false) String role) {
// 顾客角色:限制时间范围
if ("customer".equals(role) && startDate == null) {
startDate = systemConfigService.getCustomerOrderStartDate().toString();
}
return Result.success(orderService.getOrders(customerId, customerName, status, startDate, endDate, page, pageSize));
}
/**
* 获取订单详情
* 顾客只能查看自己的订单
*/
@GetMapping("/{id}")
public Result<Map<String, Object>> getOrderDetail(@PathVariable String id) {
return Result.success(orderService.getOrderDetail(id));
public Result<Map<String, Object>> getOrderDetail(@PathVariable String id,
@RequestHeader("X-User-Id") String userId,
@RequestHeader(value = "X-User-Role", required = false) String role) {
return Result.success(orderService.getOrderDetail(id, userId, role));
}
/**
@@ -65,7 +84,12 @@ public class OrderController {
*/
@PutMapping("/{id}/cancel")
public Result<Void> cancelOrder(@PathVariable String id,
@RequestHeader("X-User-Id") String operatorId) {
@RequestHeader("X-User-Id") String operatorId,
@RequestHeader(value = "X-User-Role", required = false) String role) {
// 顾客不能取消订单
if ("customer".equals(role)) {
return Result.error("顾客账号不允许取消订单");
}
orderService.cancelOrder(id, operatorId);
return Result.success();
}
@@ -75,18 +99,58 @@ public class OrderController {
*/
@PutMapping("/{id}/refund")
public Result<Void> refundOrder(@PathVariable String id,
@RequestHeader("X-User-Id") String operatorId) {
@RequestHeader("X-User-Id") String operatorId,
@RequestHeader(value = "X-User-Role", required = false) String role) {
// 顾客不能退款
if ("customer".equals(role)) {
return Result.error("顾客账号不允许退款操作");
}
orderService.refundOrder(id, operatorId);
return Result.success();
}
/**
* 更新订单状态(确认完成/取消)
*/
@PutMapping("/{id}/status")
public Result<Void> updateOrderStatus(@PathVariable String id,
@RequestBody Map<String, Integer> params,
@RequestHeader("X-User-Id") String operatorId,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if ("customer".equals(role)) {
return Result.error("顾客账号不允许操作");
}
Integer status = params.get("status");
orderService.updateOrderStatus(id, status, operatorId);
return Result.success();
}
/**
* 更新订单(编辑)
*/
@PutMapping("/{id}")
public Result<Order> updateOrder(@PathVariable String id,
@RequestBody CreateOrderRequest request,
@RequestHeader("X-User-Id") String operatorId,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if ("customer".equals(role)) {
return Result.error("顾客账号不允许操作");
}
return Result.success(orderService.updateOrder(id, request, operatorId));
}
/**
* 订单统计
*/
@GetMapping("/statistics")
public Result<Map<String, Object>> getStatistics(
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
@RequestParam(required = false) String endDate,
@RequestHeader(value = "X-User-Role", required = false) String role) {
// 顾客不能查看统计
if ("customer".equals(role)) {
return Result.error("顾客账号不允许查看统计");
}
return Result.success(orderService.getStatistics(startDate, endDate));
}
}

View File

@@ -31,26 +31,38 @@ public class ProductController {
}
/**
* 新增分类
* 新增分类(仅管理员)
*/
@PostMapping("/categories")
public Result<Category> createCategory(@RequestBody Category category) {
public Result<Category> createCategory(@RequestBody Category category,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
return Result.success(productService.createCategory(category));
}
/**
* 修改分类
* 修改分类(仅管理员)
*/
@PutMapping("/categories/{id}")
public Result<Category> updateCategory(@PathVariable String id, @RequestBody Category category) {
public Result<Category> updateCategory(@PathVariable String id, @RequestBody Category category,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
return Result.success(productService.updateCategory(id, category));
}
/**
* 删除分类
* 删除分类(仅管理员)
*/
@DeleteMapping("/categories/{id}")
public Result<Void> deleteCategory(@PathVariable String id) {
public Result<Void> deleteCategory(@PathVariable String id,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
productService.deleteCategory(id);
return Result.success();
}
@@ -67,6 +79,18 @@ public class ProductController {
return Result.success(productService.getProducts(categoryId, keyword, page, pageSize));
}
/**
* 获取所有商品(包括下架的,用于管理)
*/
@GetMapping("/all")
public Result<Page<Product>> getAllProducts(
@RequestParam(required = false) String categoryId,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer pageSize) {
return Result.success(productService.getAllProducts(categoryId, keyword, page, pageSize));
}
/**
* 获取商品详情
*/
@@ -76,26 +100,38 @@ public class ProductController {
}
/**
* 新增商品
* 新增商品(仅管理员)
*/
@PostMapping
public Result<Product> createProduct(@RequestBody Product product) {
public Result<Product> createProduct(@RequestBody Product product,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
return Result.success(productService.createProduct(product));
}
/**
* 修改商品
* 修改商品(仅管理员)
*/
@PutMapping("/{id}")
public Result<Product> updateProduct(@PathVariable String id, @RequestBody Product product) {
public Result<Product> updateProduct(@PathVariable String id, @RequestBody Product product,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
return Result.success(productService.updateProduct(id, product));
}
/**
* 删除商品
* 删除商品(仅管理员)
*/
@DeleteMapping("/{id}")
public Result<Void> deleteProduct(@PathVariable String id) {
public Result<Void> deleteProduct(@PathVariable String id,
@RequestHeader(value = "X-User-Role", required = false) String role) {
if (!"admin".equals(role)) {
return Result.error("只有管理员可以操作");
}
productService.deleteProduct(id);
return Result.success();
}

View File

@@ -0,0 +1,95 @@
package com.example.building.controller;
import com.example.building.common.Result;
import com.example.building.entity.Order;
import com.example.building.entity.OrderItem;
import com.example.building.mapper.OrderItemMapper;
import com.example.building.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 公开订单查看控制器
* 无需权限认证,用于客户通过分享链接查看订单
*/
@RestController
@RequestMapping("/api/v1/public")
public class PublicOrderController {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
/**
* 通过订单号和客户ID查询订单详情
* 公开接口需同时提供订单号和客户ID才能查看
*/
@GetMapping("/orders/{orderNo}")
public Result<Map<String, Object>> getOrderByNo(
@PathVariable String orderNo,
@RequestParam String customerId) {
// 查询订单
Order order = orderMapper.selectOne(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Order>()
.eq(Order::getOrderNo, orderNo)
.eq(Order::getCustomerId, customerId)
);
if (order == null) {
return Result.error("订单不存在");
}
// 查询订单明细
List<OrderItem> items = orderItemMapper.selectList(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, order.getOrderId())
);
// 构建返回数据(只返回公开信息)
Map<String, Object> result = new HashMap<>();
result.put("orderNo", order.getOrderNo());
result.put("status", order.getStatus());
result.put("statusText", getStatusText(order.getStatus()));
result.put("customerName", order.getCustomerName() != null ? order.getCustomerName() : "散客");
result.put("customerPhone", order.getCustomerPhone());
result.put("totalAmount", order.getTotalAmount());
result.put("discountAmount", order.getDiscountAmount());
result.put("actualAmount", order.getActualAmount());
result.put("paymentMethod", getPaymentText(order.getPaymentMethod()));
result.put("createdAt", order.getCreatedAt());
result.put("items", items);
result.put("remark", order.getRemark());
return Result.success(result);
}
private String getStatusText(Integer status) {
if (status == null) return "未知";
switch (status) {
case 0: return "进行中";
case 1: return "已完成";
case 2: return "已取消";
case 3: return "退款中";
case 4: return "已退款";
case 9: return "退货中";
default: return "未知";
}
}
private String getPaymentText(String method) {
if (method == null) return "-";
switch (method) {
case "cash": return "现金";
case "wechat": return "微信";
case "alipay": return "支付宝";
default: return method;
}
}
}

View File

@@ -0,0 +1,57 @@
package com.example.building.controller;
import com.example.building.common.Result;
import com.example.building.entity.SystemConfig;
import com.example.building.mapper.SystemConfigMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 系统配置控制器
*/
@RestController
@RequestMapping("/api/v1/system-config")
public class SystemConfigController {
@Autowired
private SystemConfigMapper systemConfigMapper;
/**
* 获取所有配置
*/
@GetMapping
public Result<Map<String, String>> getAllConfig() {
List<SystemConfig> configs = systemConfigMapper.selectList(null);
Map<String, String> result = new HashMap<>();
for (SystemConfig config : configs) {
result.put(config.getConfigKey(), config.getConfigValue());
}
return Result.success(result);
}
/**
* 更新配置
*/
@PutMapping
public Result<Void> updateConfig(@RequestBody SystemConfig config) {
// 检查是否存在
SystemConfig existing = systemConfigMapper.selectOne(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SystemConfig>()
.eq(SystemConfig::getConfigKey, config.getConfigKey())
);
if (existing != null) {
existing.setConfigValue(config.getConfigValue());
existing.setRemark(config.getRemark());
systemConfigMapper.updateById(existing);
} else {
config.setConfigId(java.util.UUID.randomUUID().toString());
systemConfigMapper.insert(config);
}
return Result.success();
}
}

View File

@@ -2,6 +2,7 @@ package com.example.building.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@@ -29,10 +30,15 @@ public class CreateOrderRequest {
private List<OrderItemDTO> items;
/**
* 折扣率(百分比), 默认100
* 折扣率(百分比), 默认100(保留此逻辑)
*/
private BigDecimal discountRate;
/**
* 优惠金额(元)
*/
private BigDecimal discountMoney;
/**
* 备注
*/
@@ -64,5 +70,20 @@ public class CreateOrderRequest {
* 销售单价(用户可自定义)
*/
private BigDecimal price;
/**
* 长度(cm)
*/
private BigDecimal length;
/**
* 宽度(cm)
*/
private BigDecimal width;
/**
* 面积(m²)
*/
private BigDecimal area;
}
}

View File

@@ -31,6 +31,16 @@ public class Customer {
*/
private String wechatOpenid;
/**
* 微信昵称
*/
private String wechatNickname;
/**
* 微信头像
*/
private String wechatAvatar;
/**
* 地址
*/
@@ -41,6 +51,11 @@ public class Customer {
*/
private String remark;
/**
* 客户种类: customer=顾客, carpenter=木匠, company=装修公司
*/
private String type;
/**
* 累计消费金额
*/
@@ -51,6 +66,11 @@ public class Customer {
*/
private String createdBy;
/**
* 最后登录时间(用于订单客户排序)
*/
private LocalDateTime lastLoginAt;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdAt;

View File

@@ -57,6 +57,11 @@ public class Order {
*/
private BigDecimal discountAmount;
/**
* 优惠金额(元,用户输入)
*/
private BigDecimal discountMoney;
/**
* 实收金额
* 计算公式: totalAmount - discountAmount
@@ -64,12 +69,12 @@ public class Order {
private BigDecimal actualAmount;
/**
* 折扣率(百分比)
* 折扣率(百分比,保留此逻辑)
*/
private BigDecimal discountRate;
/**
* 状态: 1已完成 2已取消 3退款中 4已退款
* 状态: 0未完成 1已完成 2已取消 3退款中 4已退款
*/
private Integer status;
@@ -98,4 +103,10 @@ public class Order {
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updatedAt;
/**
* 删除标记: 0未删除 1已删除
*/
@TableField("deleted")
private Integer deleted;
}

View File

@@ -26,6 +26,11 @@ public class OrderItem {
*/
private String productId;
/**
* 分类ID(冗余)
*/
private String categoryId;
/**
* 商品名称(冗余)
*/
@@ -41,11 +46,36 @@ public class OrderItem {
*/
private String unit;
/**
* 成本价(冗余)
*/
private BigDecimal costPrice;
/**
* 销售单价(当时标价)
*/
private BigDecimal price;
/**
* 商品图片URL(冗余)
*/
private String imageUrl;
/**
* 商品条码(冗余)
*/
private String barcode;
/**
* 库存预警阈值(冗余)
*/
private Integer stockAlert;
/**
* 商品描述(冗余)
*/
private String description;
/**
* 数量
*/
@@ -56,6 +86,21 @@ public class OrderItem {
*/
private BigDecimal subtotal;
/**
* 长度(cm)
*/
private BigDecimal length;
/**
* 宽度(cm)
*/
private BigDecimal width;
/**
* 面积(m²)
*/
private BigDecimal area;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdAt;
}

View File

@@ -5,6 +5,8 @@ import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 商品实体类
@@ -71,6 +73,9 @@ public class Product {
*/
private Integer status;
@TableField(exist = false)
private List<Map<String, Object>> attributes;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdAt;

View File

@@ -0,0 +1,38 @@
package com.example.building.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 系统配置实体
*/
@Data
@TableName("system_config")
public class SystemConfig {
@TableId(type = IdType.ASSIGN_UUID)
private String configId;
/**
* 配置键
*/
private String configKey;
/**
* 配置值
*/
private String configValue;
/**
* 备注
*/
private String remark;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,9 @@
package com.example.building.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.building.entity.SystemConfig;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
}

View File

@@ -19,8 +19,11 @@ public interface AuthService {
/**
* 微信扫码登录
* @param code 微信授权码
* @param nickname 微信昵称
* @param avatar 微信头像
*/
Map<String, Object> wechatLogin(String code);
Map<String, Object> wechatLogin(String code, String nickname, String avatar);
/**
* 支付宝扫码登录

View File

@@ -11,7 +11,7 @@ public interface CustomerService {
/**
* 客户列表
*/
Page<Customer> getCustomers(String keyword, Integer page, Integer pageSize);
Page<Customer> getCustomers(String keyword, String type, Integer page, Integer pageSize);
/**
* 客户详情

View File

@@ -24,12 +24,13 @@ public interface OrderService {
/**
* 获取订单列表
*/
Page<Order> getOrders(String customerId, Integer status, String startDate, String endDate, Integer page, Integer pageSize);
Page<Order> getOrders(String customerId, String customerName, Integer status, String startDate, String endDate, Integer page, Integer pageSize);
/**
* 获取订单详情(含明细)
* 顾客只能查看自己的订单
*/
Map<String, Object> getOrderDetail(String orderId);
Map<String, Object> getOrderDetail(String orderId, String userId, String role);
/**
* 取消订单
@@ -41,6 +42,16 @@ public interface OrderService {
*/
void refundOrder(String orderId, String operatorId);
/**
* 更新订单状态
*/
void updateOrderStatus(String orderId, Integer status, String operatorId);
/**
* 更新订单(编辑)
*/
Order updateOrder(String orderId, CreateOrderRequest request, String operatorId);
/**
* 订单统计
*/

View File

@@ -37,6 +37,11 @@ public interface ProductService {
*/
Page<Product> getProducts(String categoryId, String keyword, Integer page, Integer pageSize);
/**
* 获取所有商品(包括下架的)
*/
Page<Product> getAllProducts(String categoryId, String keyword, Integer page, Integer pageSize);
/**
* 获取商品详情
*/

View File

@@ -0,0 +1,43 @@
package com.example.building.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.building.entity.SystemConfig;
import com.example.building.mapper.SystemConfigMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
@Service
public class SystemConfigService {
@Autowired
private SystemConfigMapper systemConfigMapper;
/**
* 获取顾客订单可见天数配置
* 默认180天半年
*/
public int getCustomerOrderVisibleDays() {
LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SystemConfig::getConfigKey, "customer_order_visible_days");
SystemConfig config = systemConfigMapper.selectOne(wrapper);
if (config != null && config.getConfigValue() != null) {
try {
return Integer.parseInt(config.getConfigValue());
} catch (NumberFormatException e) {
return 180;
}
}
return 180; // 默认半年
}
/**
* 获取顾客可见订单开始日期
*/
public LocalDate getCustomerOrderStartDate() {
int days = getCustomerOrderVisibleDays();
return LocalDate.now().minusDays(days);
}
}

View File

@@ -1,8 +1,11 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.building.common.JwtUtil;
import com.example.building.entity.Customer;
import com.example.building.entity.User;
import com.example.building.mapper.CustomerMapper;
import com.example.building.mapper.UserMapper;
import com.example.building.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -10,6 +13,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -25,6 +29,9 @@ public class AuthServiceImpl implements AuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private CustomerMapper customerMapper;
@Autowired
private JwtUtil jwtUtil;
@@ -81,25 +88,49 @@ public class AuthServiceImpl implements AuthService {
* 实际生产中需要调用微信API获取openid
*/
@Override
public Map<String, Object> wechatLogin(String code) {
public Map<String, Object> wechatLogin(String code, String nickname, String avatar) {
// TODO: 调用微信API获取openid
// String openid = wechatService.getOpenId(code);
String openid = "wechat_" + code;
// 查询用户,不存在则创建
User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getWechatOpenid, openid));
if (user == null) {
user = new User();
user.setUserId(UUID.randomUUID().toString());
user.setWechatOpenid(openid);
user.setUsername("微信用户");
user.setRole("sales");
user.setStatus(1);
userMapper.insert(user);
// 查询或创建顾客
Customer customer = customerMapper.selectOne(new LambdaQueryWrapper<Customer>()
.eq(Customer::getWechatOpenid, openid));
if (customer == null) {
// 新顾客
customer = new Customer();
customer.setCustomerId(UUID.randomUUID().toString());
customer.setWechatOpenid(openid);
customer.setName(nickname != null ? nickname : "微信用户");
customer.setWechatNickname(nickname);
customer.setWechatAvatar(avatar);
customer.setTotalAmount(new java.math.BigDecimal("0"));
customer.setLastLoginAt(LocalDateTime.now());
customerMapper.insert(customer);
} else {
// 更新已有顾客信息
LambdaUpdateWrapper<Customer> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Customer::getCustomerId, customer.getCustomerId());
if (nickname != null) {
updateWrapper.set(Customer::getWechatNickname, nickname);
}
if (avatar != null) {
updateWrapper.set(Customer::getWechatAvatar, avatar);
}
updateWrapper.set(Customer::getLastLoginAt, LocalDateTime.now());
customerMapper.update(null, updateWrapper);
customer.setLastLoginAt(LocalDateTime.now());
}
return generateTokens(user);
// 返回顾客信息
Map<String, Object> result = new HashMap<>();
result.put("token", "customer_token_" + customer.getCustomerId());
result.put("customerId", customer.getCustomerId());
result.put("name", customer.getName());
result.put("role", "customer");
return result;
}
/**

View File

@@ -25,7 +25,7 @@ public class CustomerServiceImpl implements CustomerService {
* 客户列表
*/
@Override
public Page<Customer> getCustomers(String keyword, Integer page, Integer pageSize) {
public Page<Customer> getCustomers(String keyword, String type, Integer page, Integer pageSize) {
Page<Customer> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Customer> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(keyword)) {
@@ -33,6 +33,10 @@ public class CustomerServiceImpl implements CustomerService {
.or()
.like(Customer::getPhone, keyword);
}
if (StringUtils.hasText(type)) {
wrapper.eq(Customer::getType, type);
}
wrapper.orderByDesc(Customer::getLastLoginAt);
wrapper.orderByDesc(Customer::getCreatedAt);
return customerMapper.selectPage(pageParam, wrapper);
}

View File

@@ -62,7 +62,8 @@ public class OrderServiceImpl implements OrderService {
order.setDiscountRate(request.getDiscountRate() != null ? request.getDiscountRate() : new BigDecimal("100"));
order.setRemark(request.getRemark());
order.setPaymentMethod(request.getPaymentMethod());
order.setStatus(1); // 完成
order.setStatus(0); // 完成
order.setDeleted(0); // 未删除
// 2. 查询客户信息(如果指定了客户)
if (request.getCustomerId() != null) {
@@ -101,25 +102,46 @@ public class OrderServiceImpl implements OrderService {
item.setItemId(UUID.randomUUID().toString());
item.setOrderId(order.getOrderId());
item.setProductId(product.getProductId());
item.setCategoryId(product.getCategoryId());
item.setProductName(product.getName());
item.setProductSpec(product.getSpec());
item.setUnit(product.getUnit());
item.setCostPrice(product.getCostPrice());
item.setImageUrl(product.getImageUrl());
item.setBarcode(product.getBarcode());
item.setStockAlert(product.getStockAlert());
item.setDescription(product.getDescription());
item.setPrice(price);
item.setQuantity(itemDTO.getQuantity());
item.setLength(itemDTO.getLength());
item.setWidth(itemDTO.getWidth());
item.setArea(itemDTO.getArea());
item.setSubtotal(subtotal);
orderItems.add(item);
// 4. 扣减库存
decreaseStock(product.getProductId(), itemDTO.getQuantity(), order.getOrderId(), operatorId);
// 4. 暂时不扣减库存,等确认完成时再扣减
// decreaseStock(product.getProductId(), itemDTO.getQuantity(), order.getOrderId(), operatorId);
}
// 设置订单原价
order.setTotalAmount(totalAmount);
// 5. 计算优惠金额和实付金额
BigDecimal discountAmount;
BigDecimal discountRate = order.getDiscountRate();
BigDecimal discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
BigDecimal discountMoney = request.getDiscountMoney();
// 如果优惠金额大于0直接使用否则用折扣率计算
if (discountMoney != null && discountMoney.compareTo(BigDecimal.ZERO) >= 0) {
discountAmount = discountMoney;
} else {
// 折扣率默认100即不打折
if (discountRate == null) {
discountRate = new BigDecimal("100");
}
discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
}
BigDecimal actualAmount = totalAmount.subtract(discountAmount);
order.setDiscountAmount(discountAmount);
@@ -150,12 +172,15 @@ public class OrderServiceImpl implements OrderService {
* 获取订单列表
*/
@Override
public Page<Order> getOrders(String customerId, Integer status, String startDate, String endDate, Integer page, Integer pageSize) {
public Page<Order> getOrders(String customerId, String customerName, Integer status, String startDate, String endDate, Integer page, Integer pageSize) {
Page<Order> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
if (customerId != null) {
wrapper.eq(Order::getCustomerId, customerId);
}
if (customerName != null && !customerName.isEmpty()) {
wrapper.like(Order::getCustomerName, customerName);
}
if (status != null) {
wrapper.eq(Order::getStatus, status);
}
@@ -171,14 +196,20 @@ public class OrderServiceImpl implements OrderService {
/**
* 获取订单详情(含明细)
* 顾客只能查看自己的订单
*/
@Override
public Map<String, Object> getOrderDetail(String orderId) {
public Map<String, Object> getOrderDetail(String orderId, String userId, String role) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 顾客只能查看自己的订单
if ("customer".equals(role) && !userId.equals(order.getCustomerId())) {
throw new RuntimeException("无权查看该订单");
}
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
@@ -198,17 +229,11 @@ public class OrderServiceImpl implements OrderService {
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 1) {
if (order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许取消");
}
// 恢复库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
increaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
// 未完成订单没有扣减库存,不需要恢复
// 更新订单状态
order.setStatus(2); // 已取消
orderMapper.updateById(order);
@@ -240,6 +265,142 @@ public class OrderServiceImpl implements OrderService {
orderMapper.updateById(order);
}
/**
* 更新订单状态(确认完成/取消)
*/
@Override
@Transactional
public void updateOrderStatus(String orderId, Integer status, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 只有未完成状态可以操作
if (order.getStatus() == null || order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许操作");
}
if (status == 1) {
// 确认完成:扣减库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
decreaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
}
// 取消不需要恢复库存(因为创建时没扣减)
order.setStatus(status);
orderMapper.updateById(order);
}
/**
* 更新订单(编辑)
*/
@Override
@Transactional
public Order updateOrder(String orderId, CreateOrderRequest request, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 只有未完成状态可以编辑
if (order.getStatus() == null || order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许编辑");
}
// 更新客户信息
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
order.setCustomerId(customer.getCustomerId());
order.setCustomerName(customer.getName());
order.setCustomerPhone(customer.getPhone());
order.setCustomerWechat(customer.getWechatOpenid());
}
} else {
order.setCustomerId(null);
order.setCustomerName(null);
order.setCustomerPhone(null);
order.setCustomerWechat(null);
}
// 更新折扣和备注
order.setDiscountRate(request.getDiscountRate() != null ? request.getDiscountRate() : new BigDecimal("100"));
order.setDiscountMoney(request.getDiscountMoney() != null ? request.getDiscountMoney() : BigDecimal.ZERO);
order.setRemark(request.getRemark());
order.setPaymentMethod(request.getPaymentMethod());
// 重新计算金额
BigDecimal totalAmount = BigDecimal.ZERO;
// 删除旧的订单明细
orderItemMapper.delete(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId));
// 重新创建订单明细
List<OrderItem> orderItems = new ArrayList<>();
for (CreateOrderRequest.OrderItemDTO itemDTO : request.getItems()) {
Product product = productMapper.selectById(itemDTO.getProductId());
if (product == null) {
throw new RuntimeException("商品不存在: " + itemDTO.getProductId());
}
BigDecimal price = itemDTO.getPrice() != null ? itemDTO.getPrice() : product.getPrice();
BigDecimal subtotal = price.multiply(new BigDecimal(itemDTO.getQuantity()))
.setScale(2, RoundingMode.HALF_UP);
totalAmount = totalAmount.add(subtotal);
OrderItem item = new OrderItem();
item.setItemId(UUID.randomUUID().toString());
item.setOrderId(orderId);
item.setProductId(product.getProductId());
item.setCategoryId(product.getCategoryId());
item.setProductName(product.getName());
item.setProductSpec(product.getSpec());
item.setUnit(product.getUnit());
item.setCostPrice(product.getCostPrice());
item.setImageUrl(product.getImageUrl());
item.setBarcode(product.getBarcode());
item.setStockAlert(product.getStockAlert());
item.setDescription(product.getDescription());
item.setPrice(price);
item.setQuantity(itemDTO.getQuantity());
item.setLength(itemDTO.getLength());
item.setWidth(itemDTO.getWidth());
item.setArea(itemDTO.getArea());
item.setSubtotal(subtotal);
orderItems.add(item);
orderItemMapper.insert(item);
}
order.setTotalAmount(totalAmount);
BigDecimal discountAmount;
BigDecimal discountRate = order.getDiscountRate();
BigDecimal discountMoney = order.getDiscountMoney();
// 如果优惠金额大于等于0直接使用否则用折扣率计算
if (discountMoney != null && discountMoney.compareTo(BigDecimal.ZERO) >= 0) {
discountAmount = discountMoney;
} else {
if (discountRate == null) {
discountRate = new BigDecimal("100");
}
discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
}
BigDecimal actualAmount = totalAmount.subtract(discountAmount);
order.setDiscountAmount(discountAmount);
order.setActualAmount(actualAmount);
orderMapper.updateById(order);
return order;
}
/**
* 订单统计
*/
@@ -247,11 +408,13 @@ public class OrderServiceImpl implements OrderService {
public Map<String, Object> getStatistics(String startDate, String endDate) {
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getStatus, 1);
if (startDate != null) {
wrapper.ge(Order::getCreatedAt, startDate);
if (startDate != null && !startDate.isEmpty()) {
LocalDateTime start = LocalDate.parse(startDate, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
wrapper.ge(Order::getCreatedAt, start);
}
if (endDate != null) {
wrapper.le(Order::getCreatedAt, endDate);
if (endDate != null && !endDate.isEmpty()) {
LocalDateTime end = LocalDate.parse(endDate, DateTimeFormatter.ISO_LOCAL_DATE).atTime(23, 59, 59);
wrapper.le(Order::getCreatedAt, end);
}
List<Order> orders = orderMapper.selectList(wrapper);

View File

@@ -68,9 +68,16 @@ public class ProductServiceImpl implements ProductService {
/**
* 删除分类
* 检查是否有商品使用该分类,存在则不允许删除
*/
@Override
public void deleteCategory(String id) {
// 检查是否有商品使用该分类
Long count = productMapper.selectCount(new LambdaQueryWrapper<Product>()
.eq(Product::getCategoryId, id));
if (count > 0) {
throw new RuntimeException("该分类下有商品,无法删除");
}
categoryMapper.deleteById(id);
}
@@ -92,6 +99,20 @@ public class ProductServiceImpl implements ProductService {
return productMapper.selectPage(pageParam, wrapper);
}
@Override
public Page<Product> getAllProducts(String categoryId, String keyword, Integer page, Integer pageSize) {
Page<Product> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(categoryId)) {
wrapper.eq(Product::getCategoryId, categoryId);
}
if (StringUtils.hasText(keyword)) {
wrapper.like(Product::getName, keyword);
}
wrapper.orderByDesc(Product::getCreatedAt);
return productMapper.selectPage(pageParam, wrapper);
}
/**
* 获取商品详情
*/
@@ -130,14 +151,11 @@ public class ProductServiceImpl implements ProductService {
}
/**
* 删除商品(软删)
* 删除商品(硬删除)
*/
@Override
public void deleteProduct(String id) {
Product product = new Product();
product.setProductId(id);
product.setStatus(0);
productMapper.updateById(product);
productMapper.deleteById(id);
}
/**

View File

@@ -8,15 +8,24 @@ spring:
# PostgreSQL数据库配置
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/building_materials
username: postgres
password: postgres
url: jdbc:postgresql://${SPRING_DATASOURCE_HOST:localhost}:${SPRING_DATASOURCE_PORT:5432}/${SPRING_DATASOURCE_DB:building_materials}
username: ${SPRING_DATASOURCE_USERNAME:postgres}
password: ${SPRING_DATASOURCE_PASSWORD:postgres}
hikari:
initialization-fail-timeout: 60000
# Redis配置
# Flyway 数据库版本管理
flyway:
enabled: true
baseline-on-migrate: true
locations: classpath:db/migration
baseline-version: 0
# Redis配置 - 环境变量注入
redis:
host: localhost
port: 6379
password:
host: ${SPRING_REDIS_HOST:localhost}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:}
database: 0
timeout: 3000ms
lettuce:
@@ -34,15 +43,17 @@ mybatis-plus:
global-config:
db-config:
id-type: assign_uuid
logic-delete-field: status
logic-delete-value: 0
logic-not-delete-value: 1
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.example.building.entity
# JWT配置
# JWT配置 - 环境变量注入
jwt:
secret: building-materials-secret-key-2024
expiration: 7200000 # 2小时
refresh-expiration: 604800000 # 7天
secret: ${JWT_SECRET:building-materials-secret-key-2024}
expiration: ${JWT_EXPIRATION:7200000}
refresh-expiration: ${JWT_REFRESH_EXPIRATION:604800000}
# 日志配置
logging:

View File

@@ -0,0 +1,72 @@
-- 测试数据
-- 先删除已有数据
DELETE FROM stock_flow;
DELETE FROM order_items;
DELETE FROM orders;
DELETE FROM stock;
DELETE FROM products;
DELETE FROM categories;
DELETE FROM customers;
DELETE FROM users;
DELETE FROM system_config;
-- 用户表 - 管理员和销售
INSERT INTO users (user_id, username, phone, password, role, status) VALUES
('u-admin-001', 'admin', '13800000001', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 'admin', 1),
('u-sales-001', '张三', '13800000002', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 'sales', 1),
('u-sales-002', '李四', '13800000003', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 'sales', 1);
-- 客户表
INSERT INTO customers (customer_id, name, phone, address, remark, total_amount, created_by) VALUES
('c-001', '王五', '13900000001', '北京市朝阳区XX街道', 'VIP客户', 50000.00, 'u-admin-001'),
('c-002', '赵六', '13900000002', '上海市浦东新区XX路', '普通客户', 15000.00, 'u-sales-001'),
('c-003', '钱七', '13900000003', '广州市天河区XX大道', '新客户', 5000.00, 'u-sales-001'),
('c-004', '孙八', '13900000004', '深圳市南山区XX园', '潜在客户', 0.00, 'u-sales-002');
-- 商品分类表
INSERT INTO categories (category_id, name, parent_id, sort_order, status) VALUES
('cat-001', '钢材', NULL, 1, 1),
('cat-002', '水泥', NULL, 2, 1),
('cat-003', '木材', NULL, 3, 1),
('cat-004', '管材', NULL, 4, 1),
('cat-005', '螺纹钢', 'cat-001', 1, 1),
('cat-006', '型材', 'cat-001', 2, 1),
('cat-007', 'PC32.5', 'cat-002', 1, 1),
('cat-008', 'PC42.5', 'cat-002', 2, 1);
-- 商品表
INSERT INTO products (product_id, category_id, name, spec, unit, price, cost_price, stock_alert, status) VALUES
('p-001', 'cat-005', '螺纹钢 Φ12', '12mm', '', 4200.00, 3800.00, 10, 1),
('p-002', 'cat-005', '螺纹钢 Φ16', '16mm', '', 4100.00, 3700.00, 10, 1),
('p-003', 'cat-005', '螺纹钢 Φ20', '20mm', '', 4050.00, 3650.00, 10, 1),
('p-004', 'cat-006', 'H型钢 H200*200*8*12', '200*200', '', 3800.00, 3400.00, 5, 1),
('p-005', 'cat-007', '水泥 PC32.5', '32.5级', '', 450.00, 380.00, 50, 1),
('p-006', 'cat-008', '水泥 PC42.5', '42.5级', '', 520.00, 450.00, 50, 1),
('p-007', 'cat-003', '木材 杉木方', '4*9cm', '立方米', 1200.00, 900.00, 20, 1),
('p-008', 'cat-003', '木材 松木板', '18mm', '平方米', 85.00, 60.00, 100, 1),
('p-009', 'cat-004', 'PVC管 Φ50', '50mm', '', 12.00, 8.00, 200, 1),
('p-010', 'cat-004', 'PVC管 Φ100', '100mm', '', 28.00, 20.00, 100, 1),
('p-011', 'cat-004', 'PPR管 Φ25', '25mm', '', 8.00, 5.50, 300, 1),
('p-012', 'cat-004', '镀锌管 Φ32', '32mm', '', 22.00, 16.00, 150, 1);
-- 库存表
INSERT INTO stock (stock_id, product_id, quantity, locked_quantity) VALUES
('s-001', 'p-001', 100, 0),
('s-002', 'p-002', 80, 0),
('s-003', 'p-003', 60, 0),
('s-004', 'p-004', 25, 0),
('s-005', 'p-005', 200, 0),
('s-006', 'p-006', 150, 0),
('s-007', 'p-007', 30, 0),
('s-008', 'p-008', 500, 0),
('s-009', 'p-009', 1000, 0),
('s-010', 'p-010', 500, 0),
('s-011', 'p-011', 2000, 0),
('s-012', 'p-012', 800, 0);
-- 系统配置表
INSERT INTO system_config (config_id, config_key, config_value, remark) VALUES
('cfg-001', 'order_view_days', '180', '顾客可查看订单天数'),
('cfg-002', 'default_discount_rate', '100', '默认折扣率'),
('cfg-003', 'stock_alert_enabled', 'true', '是否启用库存预警'),
('cfg-004', 'wechat_enabled', 'true', '是否启用微信登录');

View File

@@ -0,0 +1,7 @@
-- 订单明细表增加商品冗余字段
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS category_id VARCHAR(36);
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS cost_price DECIMAL(10,2);
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS image_url VARCHAR(500);
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS barcode VARCHAR(50);
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS stock_alert INTEGER;
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS description TEXT;

View File

@@ -0,0 +1,4 @@
-- 订单明细表外键移除级联删除(商品信息已冗余保存)
ALTER TABLE order_items DROP CONSTRAINT IF EXISTS order_items_product_id_fkey;
ALTER TABLE order_items ADD CONSTRAINT order_items_product_id_fkey
FOREIGN KEY (product_id) REFERENCES products(product_id);

View File

@@ -0,0 +1,15 @@
-- 安全处理订单明细外键约束使用IF EXISTS避免重复执行报错
-- 先检查约束是否存在,存在则删除
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'order_items_product_id_fkey'
AND table_name = 'order_items'
) THEN
ALTER TABLE order_items DROP CONSTRAINT IF EXISTS order_items_product_id_fkey;
END IF;
END $$;
-- 重新添加外键约束(不级联删除)
ALTER TABLE order_items ADD CONSTRAINT order_items_product_id_fkey
FOREIGN KEY (product_id) REFERENCES products(product_id);

View File

@@ -0,0 +1,142 @@
-- 建材销售管家数据库表结构
-- 创建数据库: create database building_materials;
-- 用户表
CREATE TABLE IF NOT EXISTS users (
user_id VARCHAR(36) PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
phone VARCHAR(20),
password VARCHAR(255) NOT NULL,
wechat_openid VARCHAR(128),
wechat_unionid VARCHAR(128),
alipay_openid VARCHAR(128),
role VARCHAR(20) NOT NULL DEFAULT 'sales',
status INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 客户表
CREATE TABLE IF NOT EXISTS customers (
customer_id VARCHAR(36) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
phone VARCHAR(20),
wechat_openid VARCHAR(128),
wechat_nickname VARCHAR(100),
wechat_avatar VARCHAR(500),
address VARCHAR(500),
remark TEXT,
total_amount DECIMAL(12,2) DEFAULT 0,
created_by VARCHAR(36),
last_login_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 商品分类表
CREATE TABLE IF NOT EXISTS categories (
category_id VARCHAR(36) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
parent_id VARCHAR(36),
sort_order INTEGER DEFAULT 0,
icon VARCHAR(255),
status INTEGER DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 商品表
CREATE TABLE IF NOT EXISTS products (
product_id VARCHAR(36) PRIMARY KEY,
category_id VARCHAR(36),
name VARCHAR(100) NOT NULL,
spec VARCHAR(100),
unit VARCHAR(20) DEFAULT '',
price DECIMAL(10,2) NOT NULL,
cost_price DECIMAL(10,2),
image_url VARCHAR(500),
barcode VARCHAR(50),
stock_alert INTEGER DEFAULT 10,
description TEXT,
status INTEGER DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(category_id)
);
-- 订单表
CREATE TABLE IF NOT EXISTS orders (
order_id VARCHAR(36) PRIMARY KEY,
order_no VARCHAR(32) NOT NULL UNIQUE,
customer_id VARCHAR(36),
customer_name VARCHAR(100),
customer_phone VARCHAR(20),
customer_wechat VARCHAR(128),
total_amount DECIMAL(12,2) NOT NULL,
discount_amount DECIMAL(12,2) DEFAULT 0,
discount_money DECIMAL(12,2) DEFAULT 0,
actual_amount DECIMAL(12,2) NOT NULL,
discount_rate DECIMAL(5,2) DEFAULT 100,
status INTEGER NOT NULL DEFAULT 1,
payment_method VARCHAR(20),
remark TEXT,
operator_id VARCHAR(36),
operator_name VARCHAR(50),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- 订单明细表
CREATE TABLE IF NOT EXISTS order_items (
item_id VARCHAR(36) PRIMARY KEY,
order_id VARCHAR(36) NOT NULL,
product_id VARCHAR(36) NOT NULL,
product_name VARCHAR(100),
product_spec VARCHAR(100),
unit VARCHAR(20),
price DECIMAL(10,2) NOT NULL,
quantity INTEGER NOT NULL,
subtotal DECIMAL(12,2) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- 库存表
CREATE TABLE IF NOT EXISTS stock (
stock_id VARCHAR(36) PRIMARY KEY,
product_id VARCHAR(36) NOT NULL UNIQUE,
warehouse_id VARCHAR(36),
quantity INTEGER NOT NULL DEFAULT 0,
locked_quantity INTEGER DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- 库存流水表
CREATE TABLE IF NOT EXISTS stock_flow (
flow_id VARCHAR(36) PRIMARY KEY,
product_id VARCHAR(36) NOT NULL,
type INTEGER NOT NULL,
quantity INTEGER NOT NULL,
before_quantity INTEGER,
after_quantity INTEGER,
related_id VARCHAR(36),
related_type VARCHAR(20),
operator_id VARCHAR(36),
remark TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- 系统配置表
CREATE TABLE IF NOT EXISTS system_config (
config_id VARCHAR(36) PRIMARY KEY,
config_key VARCHAR(50) NOT NULL UNIQUE,
config_value TEXT,
remark VARCHAR(200),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -0,0 +1,2 @@
-- 订单表增加 discount_money 字段
ALTER TABLE orders ADD COLUMN discount_money DECIMAL(12,2) DEFAULT 0;

View File

@@ -0,0 +1,2 @@
-- 增加 deleted 字段用于逻辑删除
ALTER TABLE orders ADD COLUMN deleted INTEGER DEFAULT 0;

View File

@@ -0,0 +1,2 @@
-- 客户手机号唯一索引
CREATE UNIQUE INDEX idx_customer_phone ON customers(phone);

View File

@@ -0,0 +1,2 @@
-- 为客户表增加种类字段
ALTER TABLE customers ADD COLUMN type VARCHAR(20) DEFAULT 'customer';

View File

@@ -0,0 +1,11 @@
-- 打印机表
CREATE TABLE IF NOT EXISTS printers (
printer_id VARCHAR(36) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
ip VARCHAR(50) NOT NULL,
port INTEGER DEFAULT 9100,
is_default INTEGER DEFAULT 0,
status INTEGER DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -0,0 +1,4 @@
-- 删除种类属性相关表和字段(回滚 V7
DROP TABLE IF EXISTS product_attributes;
DROP TABLE IF EXISTS category_attributes;
ALTER TABLE products DROP COLUMN IF EXISTS default_unit;

View File

@@ -0,0 +1,14 @@
-- 修改商品表外键约束,级联删除(库存、库存流水)
-- 但订单明细不级联删除,因为已冗余保存商品信息
ALTER TABLE stock DROP CONSTRAINT IF EXISTS stock_product_id_fkey;
ALTER TABLE stock ADD CONSTRAINT stock_product_id_fkey
FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE;
ALTER TABLE stock_flow DROP CONSTRAINT IF EXISTS stock_flow_product_id_fkey;
ALTER TABLE stock_flow ADD CONSTRAINT stock_flow_product_id_fkey
FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE;
-- 订单明细不再级联删除(已冗余保存商品信息)
ALTER TABLE order_items DROP CONSTRAINT IF EXISTS order_items_product_id_fkey;
ALTER TABLE order_items ADD CONSTRAINT order_items_product_id_fkey
FOREIGN KEY (product_id) REFERENCES products(product_id);