Compare commits
No commits in common. "3c04019af4f29deff170ec5c77a74c88e8f90692" and "e230317fe9b42f756d4abdb0b33bc54bb42fbb57" have entirely different histories.
3c04019af4
...
e230317fe9
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
target/
|
|
||||||
uploads/
|
|
||||||
201
LICENSE
201
LICENSE
@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
36
README.en.md
36
README.en.md
@ -1,36 +0,0 @@
|
|||||||
# petstore-backend
|
|
||||||
|
|
||||||
#### Description
|
|
||||||
宠伴生活馆
|
|
||||||
|
|
||||||
#### Software Architecture
|
|
||||||
Software architecture description
|
|
||||||
|
|
||||||
#### Installation
|
|
||||||
|
|
||||||
1. xxxx
|
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### Instructions
|
|
||||||
|
|
||||||
1. xxxx
|
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### Contribution
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create Feat_xxx branch
|
|
||||||
3. Commit your code
|
|
||||||
4. Create Pull Request
|
|
||||||
|
|
||||||
|
|
||||||
#### Gitee Feature
|
|
||||||
|
|
||||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
|
||||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
|
||||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
|
||||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
|
||||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
|
||||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
|
||||||
35
README.md
35
README.md
@ -1,37 +1,2 @@
|
|||||||
# petstore-backend
|
# petstore-backend
|
||||||
|
|
||||||
#### 介绍
|
|
||||||
宠伴生活馆
|
|
||||||
|
|
||||||
#### 软件架构
|
|
||||||
软件架构说明
|
|
||||||
|
|
||||||
|
|
||||||
#### 安装教程
|
|
||||||
|
|
||||||
1. xxxx
|
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### 使用说明
|
|
||||||
|
|
||||||
1. xxxx
|
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### 参与贡献
|
|
||||||
|
|
||||||
1. Fork 本仓库
|
|
||||||
2. 新建 Feat_xxx 分支
|
|
||||||
3. 提交代码
|
|
||||||
4. 新建 Pull Request
|
|
||||||
|
|
||||||
|
|
||||||
#### 特技
|
|
||||||
|
|
||||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
|
||||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
|
||||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
|
||||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
|
||||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
|
||||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
|
||||||
|
|||||||
96
pom.xml
96
pom.xml
@ -1,96 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>3.2.3</version>
|
|
||||||
<relativePath/>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>com.petstore</groupId>
|
|
||||||
<artifactId>petstore-backend</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
<name>petstore-backend</name>
|
|
||||||
<description>宠伴生活馆 后端服务</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<java.version>17</java.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.mysql</groupId>
|
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.44</version>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.13.0</version>
|
|
||||||
<configuration>
|
|
||||||
<source>17</source>
|
|
||||||
<target>17</target>
|
|
||||||
<fork>true</fork>
|
|
||||||
<compilerArgs>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
|
||||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
|
||||||
</compilerArgs>
|
|
||||||
<annotationProcessorPaths>
|
|
||||||
<path>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.44</version>
|
|
||||||
</path>
|
|
||||||
</annotationProcessorPaths>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<excludes>
|
|
||||||
<exclude>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
</exclude>
|
|
||||||
</excludes>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
package com.petstore;
|
|
||||||
|
|
||||||
import com.petstore.service.ServiceTypeService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.boot.CommandLineRunner;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class PetstoreApplication {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println(">>> working dir: " + System.getProperty("user.dir"));
|
|
||||||
SpringApplication.run(PetstoreApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
CommandLineRunner initRunner(ServiceTypeService serviceTypeService, JdbcTemplate jdbc) {
|
|
||||||
return args -> {
|
|
||||||
serviceTypeService.initDefaults();
|
|
||||||
// appointment_id 改为允许 NULL(支持不挂预约直接填报告)
|
|
||||||
jdbc.execute("ALTER TABLE t_report MODIFY COLUMN appointment_id BIGINT NULL");
|
|
||||||
// 修复旧图片URL:/2026/xxx → /api/upload/image/2026/xxx
|
|
||||||
jdbc.execute("UPDATE t_report SET before_photo = REPLACE(before_photo, 'http://localhost:8080/2026/', '/api/upload/image/2026/') WHERE before_photo LIKE 'http://localhost:8080/2026/%'");
|
|
||||||
jdbc.execute("UPDATE t_report SET after_photo = REPLACE(after_photo, 'http://localhost:8080/2026/', '/api/upload/image/2026/') WHERE after_photo LIKE 'http://localhost:8080/2026/%'");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
package com.petstore.config;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
|
||||||
import org.springframework.web.filter.CorsFilter;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class CorsConfig {
|
|
||||||
@Bean
|
|
||||||
public CorsFilter corsFilter() {
|
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
|
||||||
config.setAllowCredentials(true);
|
|
||||||
config.addAllowedOriginPattern("*");
|
|
||||||
config.addAllowedHeader("*");
|
|
||||||
config.addAllowedMethod("*");
|
|
||||||
|
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
|
||||||
source.registerCorsConfiguration("/**", config);
|
|
||||||
return new CorsFilter(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
package com.petstore.config;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Value("${upload.path:uploads}")
|
|
||||||
private String uploadPath;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
||||||
// 硬编码绝对路径,确保能找到文件
|
|
||||||
String uploadDir = "/Users/wac/Desktop/www/_src/petstore/backend/uploads/";
|
|
||||||
System.out.println(">>> WebConfig uploadDir: " + uploadDir);
|
|
||||||
System.out.println(">>> /2026 exists: " + new File(uploadDir + "2026/04/01/").exists());
|
|
||||||
registry.addResourceHandler("/uploads/**")
|
|
||||||
.addResourceLocations("file:" + uploadDir);
|
|
||||||
registry.addResourceHandler("/2026/**")
|
|
||||||
.addResourceLocations("file:" + uploadDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
package com.petstore.config;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class WechatConfig {
|
|
||||||
// TODO: 替换为实际的微信开放平台 AppID 和 AppSecret
|
|
||||||
@Value("${wechat.appid:YOUR_APPID}")
|
|
||||||
private String appid;
|
|
||||||
|
|
||||||
@Value("${wechat.appsecret:YOUR_APPSECRET}")
|
|
||||||
private String appsecret;
|
|
||||||
|
|
||||||
@Value("${wechat.redirect_uri:http://localhost:8080/api/wechat/callback}")
|
|
||||||
private String redirectUri;
|
|
||||||
|
|
||||||
public String getAppid() { return appid; }
|
|
||||||
public String getAppsecret() { return appsecret; }
|
|
||||||
public String getRedirectUri() { return redirectUri; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取微信授权跳转地址
|
|
||||||
*/
|
|
||||||
public String getAuthorizeUrl() {
|
|
||||||
return "https://open.weixin.qq.com/connect/qrconnect" +
|
|
||||||
"?appid=" + appid +
|
|
||||||
"&redirect_uri=" + redirectUri +
|
|
||||||
"&response_type=code" +
|
|
||||||
"&scope=snsapi_login" +
|
|
||||||
"&state=petstore#wechat_redirect";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.entity.Appointment;
|
|
||||||
import com.petstore.service.AppointmentService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/appointment")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class AppointmentController {
|
|
||||||
private final AppointmentService appointmentService;
|
|
||||||
|
|
||||||
/** 获取预约列表(员工查自己/老板查全店) */
|
|
||||||
@GetMapping("/list")
|
|
||||||
public Map<String, Object> list(
|
|
||||||
@RequestParam(required = false) Long userId,
|
|
||||||
@RequestParam(required = false) Long storeId,
|
|
||||||
@RequestParam(required = false) String status) {
|
|
||||||
|
|
||||||
List<Appointment> appointments;
|
|
||||||
if (storeId != null) {
|
|
||||||
appointments = (status != null && !status.isEmpty())
|
|
||||||
? appointmentService.getByStoreIdAndStatus(storeId, status)
|
|
||||||
: appointmentService.getByStoreId(storeId);
|
|
||||||
} else if (userId != null) {
|
|
||||||
appointments = (status != null && !status.isEmpty())
|
|
||||||
? appointmentService.getByUserIdAndStatus(userId, status)
|
|
||||||
: appointmentService.getByUserId(userId);
|
|
||||||
} else {
|
|
||||||
return Map.of("code", 400, "message", "userId或storeId必填");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Map.of("code", 200, "data", appointments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 创建预约 */
|
|
||||||
@PostMapping("/create")
|
|
||||||
public Map<String, Object> create(@RequestBody Map<String, Object> params) {
|
|
||||||
Appointment appointment = new Appointment();
|
|
||||||
appointment.setPetName(params.get("petName").toString());
|
|
||||||
appointment.setPetType(params.get("petType").toString());
|
|
||||||
appointment.setServiceType(params.get("serviceType").toString());
|
|
||||||
|
|
||||||
String timeStr = params.get("appointmentTime").toString();
|
|
||||||
appointment.setAppointmentTime(java.time.LocalDateTime.parse(timeStr));
|
|
||||||
|
|
||||||
appointment.setStoreId(Long.valueOf(params.get("storeId").toString()));
|
|
||||||
appointment.setUserId(Long.valueOf(params.get("userId").toString()));
|
|
||||||
appointment.setStatus("new");
|
|
||||||
|
|
||||||
if (params.containsKey("remark") && params.get("remark") != null) {
|
|
||||||
appointment.setRemark(params.get("remark").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Appointment created = appointmentService.create(appointment);
|
|
||||||
return Map.of("code", 200, "message", "创建成功", "data", created);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开始服务:状态变进行中 + 指定技师 */
|
|
||||||
@PostMapping("/start")
|
|
||||||
public Map<String, Object> start(@RequestBody Map<String, Object> params) {
|
|
||||||
Long appointmentId = Long.valueOf(params.get("appointmentId").toString());
|
|
||||||
Long staffUserId = Long.valueOf(params.get("staffUserId").toString());
|
|
||||||
Appointment updated = appointmentService.startService(appointmentId, staffUserId);
|
|
||||||
if (updated != null) {
|
|
||||||
return Map.of("code", 200, "message", "已开始服务", "data", updated);
|
|
||||||
}
|
|
||||||
return Map.of("code", 404, "message", "预约不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新预约状态 */
|
|
||||||
@PutMapping("/status")
|
|
||||||
public Map<String, Object> updateStatus(@RequestParam Long id, @RequestParam String status) {
|
|
||||||
Appointment updated = appointmentService.updateStatus(id, status);
|
|
||||||
if (updated != null) {
|
|
||||||
return Map.of("code", 200, "message", "更新成功", "data", updated);
|
|
||||||
}
|
|
||||||
return Map.of("code", 404, "message", "预约不存在");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.core.io.FileSystemResource;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/upload")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class FileController {
|
|
||||||
|
|
||||||
@Value("${upload.path:uploads/}")
|
|
||||||
private String uploadPath;
|
|
||||||
|
|
||||||
@GetMapping("/image/**")
|
|
||||||
public ResponseEntity<Resource> getImage(HttpServletRequest request) throws IOException {
|
|
||||||
String path = request.getRequestURI().replace("/api/upload/image", "");
|
|
||||||
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
|
||||||
File file = new File(basePath + path);
|
|
||||||
if (!file.exists()) {
|
|
||||||
return ResponseEntity.notFound().build();
|
|
||||||
}
|
|
||||||
String contentType = Files.probeContentType(file.toPath());
|
|
||||||
if (contentType == null) contentType = "image/jpeg";
|
|
||||||
return ResponseEntity.ok()
|
|
||||||
.contentType(MediaType.parseMediaType(contentType))
|
|
||||||
.body(new FileSystemResource(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧路径:/2026/04/01/xxx.jpg
|
|
||||||
@GetMapping("/legacy/**")
|
|
||||||
public ResponseEntity<Resource> getLegacyImage(HttpServletRequest request) throws IOException {
|
|
||||||
String path = request.getRequestURI().replace("/api/upload/legacy", "");
|
|
||||||
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
|
||||||
File file = new File(basePath + path);
|
|
||||||
if (!file.exists()) {
|
|
||||||
return ResponseEntity.notFound().build();
|
|
||||||
}
|
|
||||||
String contentType = Files.probeContentType(file.toPath());
|
|
||||||
if (contentType == null) contentType = "image/jpeg";
|
|
||||||
return ResponseEntity.ok()
|
|
||||||
.contentType(MediaType.parseMediaType(contentType))
|
|
||||||
.body(new FileSystemResource(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/image")
|
|
||||||
public Map<String, Object> uploadImage(@RequestParam("file") MultipartFile file) {
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
|
|
||||||
if (file.isEmpty()) {
|
|
||||||
result.put("code", 400);
|
|
||||||
result.put("message", "文件为空");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
String contentType = file.getContentType();
|
|
||||||
if (contentType == null || !contentType.startsWith("image/")) {
|
|
||||||
result.put("code", 400);
|
|
||||||
result.put("message", "只能上传图片");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 创建上传目录
|
|
||||||
String datePath = LocalDate.now().toString().replace("-", "/");
|
|
||||||
String dirPath = uploadPath.endsWith("/") ? uploadPath + datePath : uploadPath + "/" + datePath;
|
|
||||||
File dir = new File(dirPath);
|
|
||||||
if (!dir.exists()) dir.mkdirs();
|
|
||||||
|
|
||||||
// 生成文件名
|
|
||||||
String originalFilename = file.getOriginalFilename();
|
|
||||||
String ext = "";
|
|
||||||
if (originalFilename != null && originalFilename.contains(".")) {
|
|
||||||
ext = originalFilename.substring(originalFilename.lastIndexOf("."));
|
|
||||||
}
|
|
||||||
String filename = UUID.randomUUID().toString().replace("-", "") + ext;
|
|
||||||
|
|
||||||
// 保存文件
|
|
||||||
Path filePath = Paths.get(dirPath, filename);
|
|
||||||
Files.write(filePath, file.getBytes());
|
|
||||||
|
|
||||||
// 返回访问URL(/api/upload/image/ + 日期路径 + 文件名)
|
|
||||||
String url = "/api/upload/image/" + datePath + "/" + filename;
|
|
||||||
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "上传成功");
|
|
||||||
result.put("data", Map.of("url", url));
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
result.put("code", 500);
|
|
||||||
result.put("message", "上传失败");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.entity.Report;
|
|
||||||
import com.petstore.entity.Store;
|
|
||||||
import com.petstore.service.ReportService;
|
|
||||||
import com.petstore.service.StoreService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/report")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class ReportController {
|
|
||||||
private final ReportService reportService;
|
|
||||||
private final StoreService storeService;
|
|
||||||
|
|
||||||
@Value("${app.base-url:http://localhost:8080}")
|
|
||||||
private String baseUrl;
|
|
||||||
|
|
||||||
private String fullUrl(String path) {
|
|
||||||
if (path == null || path.isEmpty()) return path;
|
|
||||||
if (path.startsWith("http")) return path;
|
|
||||||
return baseUrl + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/create")
|
|
||||||
public Map<String, Object> create(@RequestBody Report report) {
|
|
||||||
System.out.println(">>> Report create received: appointmentId=" + report.getAppointmentId() + ", userId=" + report.getUserId() + ", before=" + report.getBeforePhoto());
|
|
||||||
Report created = reportService.create(report);
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "提交成功");
|
|
||||||
result.put("data", Map.of(
|
|
||||||
"reportToken", created.getReportToken(),
|
|
||||||
"reportId", created.getId()
|
|
||||||
));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/list")
|
|
||||||
public Map<String, Object> list(@RequestParam(required = false) Long storeId,
|
|
||||||
@RequestParam(required = false) Long userId) {
|
|
||||||
List<Report> reports = reportService.list(storeId, userId);
|
|
||||||
// 附加技师名称,并补全图片URL
|
|
||||||
List<Map<String, Object>> data = reports.stream().map(r -> {
|
|
||||||
Map<String, Object> item = new HashMap<>();
|
|
||||||
item.put("id", r.getId());
|
|
||||||
item.put("appointmentId", r.getAppointmentId());
|
|
||||||
item.put("petName", r.getPetName());
|
|
||||||
item.put("serviceType", r.getServiceType());
|
|
||||||
item.put("appointmentTime", r.getAppointmentTime());
|
|
||||||
item.put("staffName", r.getStaffName());
|
|
||||||
item.put("reportToken", r.getReportToken());
|
|
||||||
item.put("createTime", r.getCreateTime());
|
|
||||||
item.put("beforePhoto", fullUrl(r.getBeforePhoto()));
|
|
||||||
item.put("afterPhoto", fullUrl(r.getAfterPhoto()));
|
|
||||||
item.put("storeId", r.getStoreId());
|
|
||||||
item.put("userId", r.getUserId());
|
|
||||||
return item;
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
return Map.of("code", 200, "data", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/get")
|
|
||||||
public Map<String, Object> getByAppointmentId(@RequestParam(required = false) Long appointmentId,
|
|
||||||
@RequestParam(required = false) String token) {
|
|
||||||
Report report = null;
|
|
||||||
if (token != null && !token.isEmpty()) {
|
|
||||||
report = reportService.getByToken(token);
|
|
||||||
} else if (appointmentId != null) {
|
|
||||||
report = reportService.getByAppointmentId(appointmentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
if (report != null) {
|
|
||||||
// 附加店铺信息
|
|
||||||
Store store = null;
|
|
||||||
if (report.getStoreId() != null) {
|
|
||||||
store = storeService.findById(report.getStoreId());
|
|
||||||
}
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("id", report.getId());
|
|
||||||
data.put("appointmentId", report.getAppointmentId());
|
|
||||||
data.put("beforePhoto", report.getBeforePhoto());
|
|
||||||
data.put("afterPhoto", report.getAfterPhoto());
|
|
||||||
data.put("remark", report.getRemark());
|
|
||||||
data.put("userId", report.getUserId());
|
|
||||||
data.put("storeId", report.getStoreId());
|
|
||||||
data.put("reportToken", report.getReportToken());
|
|
||||||
data.put("petName", report.getPetName());
|
|
||||||
data.put("serviceType", report.getServiceType());
|
|
||||||
data.put("appointmentTime", report.getAppointmentTime());
|
|
||||||
data.put("staffName", report.getStaffName());
|
|
||||||
data.put("createTime", report.getCreateTime());
|
|
||||||
if (store != null) {
|
|
||||||
Map<String, Object> storeInfo = new HashMap<>();
|
|
||||||
storeInfo.put("name", store.getName());
|
|
||||||
storeInfo.put("logo", store.getLogo());
|
|
||||||
storeInfo.put("phone", store.getPhone());
|
|
||||||
storeInfo.put("address", store.getAddress());
|
|
||||||
data.put("store", storeInfo);
|
|
||||||
}
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("data", data);
|
|
||||||
} else {
|
|
||||||
result.put("code", 404);
|
|
||||||
result.put("message", "报告不存在");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.entity.ServiceType;
|
|
||||||
import com.petstore.service.ServiceTypeService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/service-type")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class ServiceTypeController {
|
|
||||||
private final ServiceTypeService serviceTypeService;
|
|
||||||
|
|
||||||
@GetMapping("/list")
|
|
||||||
public Map<String, Object> list(@RequestParam Long storeId) {
|
|
||||||
List<ServiceType> list = serviceTypeService.getByStoreId(storeId);
|
|
||||||
return Map.of("code", 200, "data", list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/create")
|
|
||||||
public Map<String, Object> create(@RequestBody Map<String, Object> params) {
|
|
||||||
Long storeId = Long.valueOf(params.get("storeId").toString());
|
|
||||||
String name = params.get("name").toString();
|
|
||||||
ServiceType created = serviceTypeService.create(storeId, name);
|
|
||||||
return Map.of("code", 200, "data", created);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("/update")
|
|
||||||
public Map<String, Object> update(@RequestBody Map<String, Object> params) {
|
|
||||||
Long id = Long.valueOf(params.get("id").toString());
|
|
||||||
String name = params.get("name").toString();
|
|
||||||
ServiceType updated = serviceTypeService.update(id, name);
|
|
||||||
return Map.of("code", 200, "data", updated);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/delete")
|
|
||||||
public Map<String, Object> delete(@RequestParam Long id) {
|
|
||||||
serviceTypeService.delete(id);
|
|
||||||
return Map.of("code", 200, "message", "删除成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/init")
|
|
||||||
public Map<String, Object> init() {
|
|
||||||
serviceTypeService.initDefaults();
|
|
||||||
return Map.of("code", 200, "message", "初始化成功");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/sms")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class SmsController {
|
|
||||||
|
|
||||||
// TODO: 接入真实的短信服务商(阿里云/腾讯云/华为云等)
|
|
||||||
// 这里用演示模式,随机生成6位验证码
|
|
||||||
|
|
||||||
@PostMapping("/send")
|
|
||||||
public Map<String, Object> send(@RequestBody Map<String, String> params) {
|
|
||||||
String phone = params.get("phone");
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
|
|
||||||
if (phone == null || phone.length() != 11) {
|
|
||||||
result.put("code", 400);
|
|
||||||
result.put("message", "手机号格式不正确");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 演示:生成6位验证码
|
|
||||||
String code = String.format("%06d", new Random().nextInt(999999));
|
|
||||||
|
|
||||||
// TODO: 调用真实短信服务商发送验证码
|
|
||||||
// 阿里云示例: dysmsapi.aliyuncs.com
|
|
||||||
// 腾讯云示例: sms.tencentcloudapi.com
|
|
||||||
|
|
||||||
System.out.println("【宠伴生活馆】验证码:" + code + ",您正在登录,5分钟内有效。");
|
|
||||||
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "发送成功");
|
|
||||||
result.put("data", Map.of("code", code)); // 演示用,实际生产环境不应返回code
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.entity.Store;
|
|
||||||
import com.petstore.service.StoreService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/store")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class StoreController {
|
|
||||||
private final StoreService storeService;
|
|
||||||
|
|
||||||
@PostMapping("/register")
|
|
||||||
public Map<String, Object> register(@RequestBody Store store) {
|
|
||||||
Store created = storeService.create(store);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "注册成功");
|
|
||||||
result.put("data", created);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/get")
|
|
||||||
public Map<String, Object> get(@RequestParam Long id) {
|
|
||||||
Store store = storeService.findById(id);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
if (store != null) {
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("data", store);
|
|
||||||
} else {
|
|
||||||
result.put("code", 404);
|
|
||||||
result.put("message", "店铺不存在");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("/update")
|
|
||||||
public Map<String, Object> update(@RequestBody Store store) {
|
|
||||||
Store updated = storeService.update(store);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "更新成功");
|
|
||||||
result.put("data", updated);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/invite-code")
|
|
||||||
public Map<String, Object> getByInviteCode(@RequestParam String code) {
|
|
||||||
Store store = storeService.findByInviteCode(code);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
if (store != null) {
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("data", store);
|
|
||||||
} else {
|
|
||||||
result.put("code", 404);
|
|
||||||
result.put("message", "邀请码无效");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.entity.User;
|
|
||||||
import com.petstore.service.UserService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/user")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class UserController {
|
|
||||||
private final UserService userService;
|
|
||||||
|
|
||||||
/** 老板注册店铺 */
|
|
||||||
@PostMapping("/register-boss")
|
|
||||||
public Map<String, Object> registerBoss(@RequestBody Map<String, String> params) {
|
|
||||||
String storeName = params.get("storeName");
|
|
||||||
String bossName = params.get("bossName");
|
|
||||||
String phone = params.get("phone");
|
|
||||||
String password = params.get("password");
|
|
||||||
return userService.registerBoss(storeName, bossName, phone, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 登录(老板/员工) */
|
|
||||||
@PostMapping("/login")
|
|
||||||
public Map<String, Object> login(@RequestBody Map<String, String> params) {
|
|
||||||
String phone = params.get("phone");
|
|
||||||
String code = params.get("code");
|
|
||||||
return userService.login(phone, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 员工注册(邀请码方式) */
|
|
||||||
@PostMapping("/register-staff")
|
|
||||||
public Map<String, Object> registerStaff(@RequestBody Map<String, String> params) {
|
|
||||||
String phone = params.get("phone");
|
|
||||||
String password = params.get("password");
|
|
||||||
String name = params.get("name");
|
|
||||||
String inviteCode = params.get("inviteCode");
|
|
||||||
return userService.registerStaff(phone, password, name, inviteCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板:创建员工 */
|
|
||||||
@PostMapping("/create-staff")
|
|
||||||
public Map<String, Object> createStaff(@RequestBody Map<String, Object> params) {
|
|
||||||
Long storeId = Long.valueOf(params.get("storeId").toString());
|
|
||||||
String name = params.get("name").toString();
|
|
||||||
String phone = params.get("phone").toString();
|
|
||||||
return userService.createStaff(storeId, name, phone);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板:员工列表 */
|
|
||||||
@GetMapping("/staff-list")
|
|
||||||
public Map<String, Object> staffList(@RequestParam Long storeId) {
|
|
||||||
List<User> list = userService.getStaffList(storeId);
|
|
||||||
// 不返回密码
|
|
||||||
list.forEach(u -> u.setPassword(null));
|
|
||||||
return Map.of("code", 200, "data", list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板:删除员工 */
|
|
||||||
@DeleteMapping("/staff")
|
|
||||||
public Map<String, Object> deleteStaff(@RequestParam Long staffId) {
|
|
||||||
userService.deleteStaff(staffId);
|
|
||||||
return Map.of("code", 200, "message", "删除成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取用户信息 */
|
|
||||||
@GetMapping("/info")
|
|
||||||
public Map<String, Object> info(@RequestParam Long userId) {
|
|
||||||
User user = userService.findById(userId);
|
|
||||||
if (user != null) user.setPassword(null);
|
|
||||||
return Map.of("code", 200, "data", user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新用户信息(头像/姓名/手机号) */
|
|
||||||
@PutMapping("/update")
|
|
||||||
public Map<String, Object> updateUser(@RequestBody Map<String, Object> params) {
|
|
||||||
return userService.updateUser(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
package com.petstore.controller;
|
|
||||||
|
|
||||||
import com.petstore.config.WechatConfig;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/wechat")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class WechatController {
|
|
||||||
private final WechatConfig wechatConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取微信授权跳转地址
|
|
||||||
*/
|
|
||||||
@GetMapping("/authorize")
|
|
||||||
public Map<String, Object> getAuthorizeUrl() {
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("data", wechatConfig.getAuthorizeUrl());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信授权回调
|
|
||||||
* 通过 code 换取 access_token,返回用户信息
|
|
||||||
*/
|
|
||||||
@GetMapping("/callback")
|
|
||||||
public Map<String, Object> callback(@RequestParam String code, @RequestParam String state) {
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
|
|
||||||
// TODO: 通过 code 调用微信接口换取 openid 和 access_token
|
|
||||||
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
|
|
||||||
|
|
||||||
// 演示用:直接返回成功
|
|
||||||
result.put("code", 200);
|
|
||||||
result.put("message", "微信授权成功");
|
|
||||||
result.put("data", Map.of(
|
|
||||||
"openid", "demo_openid_" + code,
|
|
||||||
"nickname", "微信用户",
|
|
||||||
"avatar", ""
|
|
||||||
));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
package com.petstore.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "t_appointment")
|
|
||||||
public class Appointment {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String petName;
|
|
||||||
private String petType;
|
|
||||||
private String serviceType;
|
|
||||||
private LocalDateTime appointmentTime;
|
|
||||||
|
|
||||||
/** 状态: new/doing/done/cancel */
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
@Column(name = "store_id")
|
|
||||||
private Long storeId;
|
|
||||||
|
|
||||||
@Column(name = "user_id")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/** 技师ID,开始服务时赋值 */
|
|
||||||
@Column(name = "assigned_user_id")
|
|
||||||
private Long assignedUserId;
|
|
||||||
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@Column(name = "create_time")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@Column(name = "update_time")
|
|
||||||
private LocalDateTime updateTime;
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
package com.petstore.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "t_report")
|
|
||||||
public class Report {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@JsonProperty("appointmentId")
|
|
||||||
@Column(name = "appointment_id")
|
|
||||||
private Long appointmentId;
|
|
||||||
|
|
||||||
@Column(name = "before_photo", columnDefinition = "TEXT")
|
|
||||||
private String beforePhoto;
|
|
||||||
|
|
||||||
@Column(name = "after_photo", columnDefinition = "TEXT")
|
|
||||||
private String afterPhoto;
|
|
||||||
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@Column(name = "user_id")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@Column(name = "store_id")
|
|
||||||
private Long storeId;
|
|
||||||
|
|
||||||
@Column(name = "report_token")
|
|
||||||
private String reportToken;
|
|
||||||
|
|
||||||
private String petName;
|
|
||||||
private String serviceType;
|
|
||||||
private LocalDateTime appointmentTime;
|
|
||||||
private String staffName;
|
|
||||||
|
|
||||||
@Column(name = "create_time")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@Column(name = "update_time")
|
|
||||||
private LocalDateTime updateTime;
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
package com.petstore.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "t_service_type")
|
|
||||||
public class ServiceType {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/** 店铺ID,NULL表示系统默认 */
|
|
||||||
private Long storeId;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Column(name = "create_time")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package com.petstore.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "t_store")
|
|
||||||
public class Store {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String logo;
|
|
||||||
private String phone;
|
|
||||||
private String address;
|
|
||||||
/** 地图选点纬度(WGS84 / 各端与地图接口一致) */
|
|
||||||
private Double latitude;
|
|
||||||
/** 地图选点经度 */
|
|
||||||
private Double longitude;
|
|
||||||
private String intro;
|
|
||||||
|
|
||||||
@Column(name = "owner_id")
|
|
||||||
private Long ownerId;
|
|
||||||
|
|
||||||
@Column(name = "invite_code")
|
|
||||||
private String inviteCode;
|
|
||||||
|
|
||||||
@Column(name = "create_time")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@Column(name = "update_time")
|
|
||||||
private LocalDateTime updateTime;
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package com.petstore.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "t_user")
|
|
||||||
public class User {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
private String password;
|
|
||||||
private String name;
|
|
||||||
private String phone;
|
|
||||||
private String avatar;
|
|
||||||
|
|
||||||
@Column(name = "store_id")
|
|
||||||
private Long storeId;
|
|
||||||
|
|
||||||
/** 角色:boss / staff / customer */
|
|
||||||
private String role;
|
|
||||||
|
|
||||||
@Column(name = "create_time")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@Column(name = "update_time")
|
|
||||||
private LocalDateTime updateTime;
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
package com.petstore.mapper;
|
|
||||||
|
|
||||||
import com.petstore.entity.Appointment;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface AppointmentMapper extends JpaRepository<Appointment, Long> {
|
|
||||||
List<Appointment> findByUserId(Long userId);
|
|
||||||
List<Appointment> findByUserIdAndStatus(Long userId, String status);
|
|
||||||
List<Appointment> findByStoreId(Long storeId);
|
|
||||||
List<Appointment> findByStoreIdAndStatus(Long storeId, String status);
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
package com.petstore.mapper;
|
|
||||||
|
|
||||||
import com.petstore.entity.Report;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface ReportMapper extends JpaRepository<Report, Long> {
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package com.petstore.mapper;
|
|
||||||
|
|
||||||
import com.petstore.entity.ServiceType;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface ServiceTypeMapper extends JpaRepository<ServiceType, Long> {
|
|
||||||
List<ServiceType> findByStoreIdOrStoreIdIsNull(Long storeId);
|
|
||||||
List<ServiceType> findByStoreId(Long storeId);
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package com.petstore.mapper;
|
|
||||||
|
|
||||||
import com.petstore.entity.Store;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface StoreMapper extends JpaRepository<Store, Long> {
|
|
||||||
Store findByInviteCode(String inviteCode);
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package com.petstore.mapper;
|
|
||||||
|
|
||||||
import com.petstore.entity.User;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface UserMapper extends JpaRepository<User, Long> {
|
|
||||||
User findByUsername(String username);
|
|
||||||
User findByPhone(String phone);
|
|
||||||
List<User> findByStoreId(Long storeId);
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package com.petstore.service;
|
|
||||||
|
|
||||||
import com.petstore.entity.Appointment;
|
|
||||||
import com.petstore.mapper.AppointmentMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AppointmentService {
|
|
||||||
private final AppointmentMapper appointmentMapper;
|
|
||||||
|
|
||||||
// 员工查看自己的预约
|
|
||||||
public List<Appointment> getByUserId(Long userId) {
|
|
||||||
return appointmentMapper.findByUserId(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 老板查看本店所有预约
|
|
||||||
public List<Appointment> getByStoreId(Long storeId) {
|
|
||||||
return appointmentMapper.findByStoreId(storeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 员工按状态查
|
|
||||||
public List<Appointment> getByUserIdAndStatus(Long userId, String status) {
|
|
||||||
return appointmentMapper.findByUserIdAndStatus(userId, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 老板按状态查
|
|
||||||
public List<Appointment> getByStoreIdAndStatus(Long storeId, String status) {
|
|
||||||
return appointmentMapper.findByStoreIdAndStatus(storeId, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Appointment create(Appointment appointment) {
|
|
||||||
appointment.setCreateTime(LocalDateTime.now());
|
|
||||||
appointment.setUpdateTime(LocalDateTime.now());
|
|
||||||
return appointmentMapper.save(appointment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Appointment updateStatus(Long id, String status) {
|
|
||||||
Appointment appointment = appointmentMapper.findById(id).orElse(null);
|
|
||||||
if (appointment != null) {
|
|
||||||
appointment.setStatus(status);
|
|
||||||
appointment.setUpdateTime(LocalDateTime.now());
|
|
||||||
return appointmentMapper.save(appointment);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开始服务:状态变为进行中,同时指定技师为当前用户 */
|
|
||||||
public Appointment startService(Long appointmentId, Long staffUserId) {
|
|
||||||
Appointment appointment = appointmentMapper.findById(appointmentId).orElse(null);
|
|
||||||
if (appointment != null) {
|
|
||||||
appointment.setStatus("doing");
|
|
||||||
appointment.setAssignedUserId(staffUserId);
|
|
||||||
appointment.setUpdateTime(LocalDateTime.now());
|
|
||||||
return appointmentMapper.save(appointment);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
package com.petstore.service;
|
|
||||||
|
|
||||||
import com.petstore.entity.Appointment;
|
|
||||||
import com.petstore.entity.Report;
|
|
||||||
import com.petstore.entity.User;
|
|
||||||
import com.petstore.mapper.AppointmentMapper;
|
|
||||||
import com.petstore.mapper.ReportMapper;
|
|
||||||
import com.petstore.mapper.UserMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ReportService {
|
|
||||||
private final ReportMapper reportMapper;
|
|
||||||
private final AppointmentMapper appointmentMapper;
|
|
||||||
private final UserMapper userMapper;
|
|
||||||
|
|
||||||
public Report create(Report report) {
|
|
||||||
// 生成唯一令牌
|
|
||||||
report.setReportToken(UUID.randomUUID().toString().replace("-", ""));
|
|
||||||
|
|
||||||
// 填充冗余字段,并自动完成预约
|
|
||||||
if (report.getAppointmentId() != null) {
|
|
||||||
Appointment appt = appointmentMapper.findById(report.getAppointmentId()).orElse(null);
|
|
||||||
if (appt != null) {
|
|
||||||
report.setPetName(appt.getPetName());
|
|
||||||
report.setServiceType(appt.getServiceType());
|
|
||||||
report.setAppointmentTime(appt.getAppointmentTime());
|
|
||||||
report.setStoreId(appt.getStoreId());
|
|
||||||
// 技师取预约分配的技师(开始服务时指定的)
|
|
||||||
if (appt.getAssignedUserId() != null) {
|
|
||||||
User staff = userMapper.findById(appt.getAssignedUserId()).orElse(null);
|
|
||||||
if (staff != null) {
|
|
||||||
report.setUserId(staff.getId());
|
|
||||||
report.setStaffName(staff.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 填写完报告,自动标记预约为已完成
|
|
||||||
appt.setStatus("done");
|
|
||||||
appt.setUpdateTime(LocalDateTime.now());
|
|
||||||
appointmentMapper.save(appt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果预约没分配技师,则用当前操作人
|
|
||||||
if (report.getUserId() != null && report.getStaffName() == null) {
|
|
||||||
User staff = userMapper.findById(report.getUserId()).orElse(null);
|
|
||||||
if (staff != null) {
|
|
||||||
report.setStaffName(staff.getName());
|
|
||||||
if (report.getStoreId() == null) {
|
|
||||||
report.setStoreId(staff.getStoreId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
report.setCreateTime(LocalDateTime.now());
|
|
||||||
report.setUpdateTime(LocalDateTime.now());
|
|
||||||
return reportMapper.save(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Report getByAppointmentId(Long appointmentId) {
|
|
||||||
return reportMapper.findAll().stream()
|
|
||||||
.filter(r -> r.getAppointmentId().equals(appointmentId))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Report getByToken(String token) {
|
|
||||||
return reportMapper.findAll().stream()
|
|
||||||
.filter(r -> token.equals(r.getReportToken()))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Report> list(Long storeId, Long userId) {
|
|
||||||
return reportMapper.findAll().stream()
|
|
||||||
.filter(r -> storeId == null || storeId.equals(r.getStoreId()))
|
|
||||||
.filter(r -> userId == null || userId.equals(r.getUserId()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
package com.petstore.service;
|
|
||||||
|
|
||||||
import com.petstore.entity.ServiceType;
|
|
||||||
import com.petstore.mapper.ServiceTypeMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ServiceTypeService {
|
|
||||||
private final ServiceTypeMapper serviceTypeMapper;
|
|
||||||
|
|
||||||
/** 获取服务类型(系统默认 + 当前店铺自定义) */
|
|
||||||
public List<ServiceType> getByStoreId(Long storeId) {
|
|
||||||
return serviceTypeMapper.findByStoreIdOrStoreIdIsNull(storeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板新增服务类型 */
|
|
||||||
public ServiceType create(Long storeId, String name) {
|
|
||||||
ServiceType st = new ServiceType();
|
|
||||||
st.setStoreId(storeId);
|
|
||||||
st.setName(name);
|
|
||||||
st.setCreateTime(LocalDateTime.now());
|
|
||||||
return serviceTypeMapper.save(st);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板编辑服务类型 */
|
|
||||||
public ServiceType update(Long id, String name) {
|
|
||||||
ServiceType st = serviceTypeMapper.findById(id).orElse(null);
|
|
||||||
if (st != null) {
|
|
||||||
st.setName(name);
|
|
||||||
serviceTypeMapper.save(st);
|
|
||||||
}
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 老板删除服务类型(仅能删除自己店铺的) */
|
|
||||||
public void delete(Long id) {
|
|
||||||
serviceTypeMapper.deleteById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化系统默认服务类型(如果不存在) */
|
|
||||||
public void initDefaults() {
|
|
||||||
List<ServiceType> defaults = serviceTypeMapper.findByStoreIdOrStoreIdIsNull(null);
|
|
||||||
if (defaults.isEmpty()) {
|
|
||||||
String[] names = {"洗澡", "美容", "洗澡+美容", "剪指甲", "驱虫"};
|
|
||||||
for (String name : names) {
|
|
||||||
ServiceType st = new ServiceType();
|
|
||||||
st.setStoreId(null);
|
|
||||||
st.setName(name);
|
|
||||||
st.setCreateTime(LocalDateTime.now());
|
|
||||||
serviceTypeMapper.save(st);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package com.petstore.service;
|
|
||||||
|
|
||||||
import com.petstore.entity.Store;
|
|
||||||
import com.petstore.mapper.StoreMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class StoreService {
|
|
||||||
private final StoreMapper storeMapper;
|
|
||||||
|
|
||||||
public Store create(Store store) {
|
|
||||||
store.setInviteCode(generateInviteCode());
|
|
||||||
store.setCreateTime(LocalDateTime.now());
|
|
||||||
store.setUpdateTime(LocalDateTime.now());
|
|
||||||
return storeMapper.save(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Store findById(Long id) {
|
|
||||||
return storeMapper.findById(id).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Store findByInviteCode(String code) {
|
|
||||||
return storeMapper.findByInviteCode(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Store update(Store store) {
|
|
||||||
store.setUpdateTime(LocalDateTime.now());
|
|
||||||
return storeMapper.save(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateInviteCode() {
|
|
||||||
return UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
package com.petstore.service;
|
|
||||||
|
|
||||||
import com.petstore.entity.Store;
|
|
||||||
import com.petstore.entity.User;
|
|
||||||
import com.petstore.mapper.StoreMapper;
|
|
||||||
import com.petstore.mapper.UserMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class UserService {
|
|
||||||
private final UserMapper userMapper;
|
|
||||||
private final StoreMapper storeMapper;
|
|
||||||
|
|
||||||
public Map<String, Object> registerBoss(String storeName, String bossName, String phone, String password) {
|
|
||||||
if (userMapper.findByPhone(phone) != null) {
|
|
||||||
return Map.of("code", 400, "message", "手机号已注册");
|
|
||||||
}
|
|
||||||
|
|
||||||
Store store = new Store();
|
|
||||||
store.setName(storeName);
|
|
||||||
store.setPhone(phone);
|
|
||||||
store.setOwnerId(0L);
|
|
||||||
store.setInviteCode(UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase());
|
|
||||||
store.setCreateTime(LocalDateTime.now());
|
|
||||||
store.setUpdateTime(LocalDateTime.now());
|
|
||||||
store = storeMapper.save(store);
|
|
||||||
|
|
||||||
User boss = new User();
|
|
||||||
boss.setUsername(phone);
|
|
||||||
boss.setName(bossName);
|
|
||||||
boss.setPhone(phone);
|
|
||||||
boss.setPassword(password);
|
|
||||||
boss.setStoreId(store.getId());
|
|
||||||
boss.setRole("boss");
|
|
||||||
boss.setCreateTime(LocalDateTime.now());
|
|
||||||
boss.setUpdateTime(LocalDateTime.now());
|
|
||||||
boss = userMapper.save(boss);
|
|
||||||
|
|
||||||
store.setOwnerId(boss.getId());
|
|
||||||
storeMapper.save(store);
|
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("user", boss);
|
|
||||||
data.put("store", store);
|
|
||||||
return Map.of("code", 200, "message", "注册成功", "data", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> login(String phone, String code) {
|
|
||||||
// 演示模式:验证码 123456 万能
|
|
||||||
if (!"123456".equals(code)) {
|
|
||||||
return Map.of("code", 401, "message", "验证码错误");
|
|
||||||
}
|
|
||||||
User user = userMapper.findByPhone(phone);
|
|
||||||
if (user == null) {
|
|
||||||
// 自动注册为 C 端用户 (customer)
|
|
||||||
user = new User();
|
|
||||||
user.setUsername(phone);
|
|
||||||
user.setPhone(phone);
|
|
||||||
user.setName("微信用户" + phone.substring(7));
|
|
||||||
user.setRole("customer");
|
|
||||||
user.setCreateTime(LocalDateTime.now());
|
|
||||||
user.setUpdateTime(LocalDateTime.now());
|
|
||||||
user = userMapper.save(user);
|
|
||||||
}
|
|
||||||
Store store = null;
|
|
||||||
if (user.getStoreId() != null) {
|
|
||||||
store = storeMapper.findById(user.getStoreId()).orElse(null);
|
|
||||||
}
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("user", user);
|
|
||||||
data.put("store", store);
|
|
||||||
return Map.of("code", 200, "message", "登录成功", "data", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> registerStaff(String phone, String password, String name, String inviteCode) {
|
|
||||||
if (userMapper.findByPhone(phone) != null) {
|
|
||||||
return Map.of("code", 400, "message", "手机号已注册");
|
|
||||||
}
|
|
||||||
Store store = storeMapper.findByInviteCode(inviteCode);
|
|
||||||
if (store == null) {
|
|
||||||
return Map.of("code", 400, "message", "邀请码无效");
|
|
||||||
}
|
|
||||||
User staff = new User();
|
|
||||||
staff.setUsername(phone);
|
|
||||||
staff.setPhone(phone);
|
|
||||||
staff.setPassword(password);
|
|
||||||
staff.setName(name);
|
|
||||||
staff.setStoreId(store.getId());
|
|
||||||
staff.setRole("staff");
|
|
||||||
staff.setCreateTime(LocalDateTime.now());
|
|
||||||
staff.setUpdateTime(LocalDateTime.now());
|
|
||||||
staff = userMapper.save(staff);
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("user", staff);
|
|
||||||
data.put("store", store);
|
|
||||||
return Map.of("code", 200, "message", "注册成功", "data", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> createStaff(Long storeId, String name, String phone) {
|
|
||||||
if (storeId == null) {
|
|
||||||
return Map.of("code", 400, "message", "店铺ID不能为空");
|
|
||||||
}
|
|
||||||
if (userMapper.findByPhone(phone) != null) {
|
|
||||||
return Map.of("code", 400, "message", "手机号已存在");
|
|
||||||
}
|
|
||||||
String pwd = String.format("%06d", (int)(Math.random() * 999999));
|
|
||||||
User staff = new User();
|
|
||||||
staff.setUsername(phone);
|
|
||||||
staff.setName(name);
|
|
||||||
staff.setPhone(phone);
|
|
||||||
staff.setPassword(pwd);
|
|
||||||
staff.setStoreId(storeId);
|
|
||||||
staff.setRole("staff");
|
|
||||||
staff.setCreateTime(LocalDateTime.now());
|
|
||||||
staff.setUpdateTime(LocalDateTime.now());
|
|
||||||
staff = userMapper.save(staff);
|
|
||||||
return Map.of("code", 200, "message", "创建成功,初始密码:" + pwd, "data", staff);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<User> getStaffList(Long storeId) {
|
|
||||||
return userMapper.findByStoreId(storeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteStaff(Long staffId) {
|
|
||||||
userMapper.deleteById(staffId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public User findById(Long id) {
|
|
||||||
return userMapper.findById(id).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> updateUser(Map<String, Object> params) {
|
|
||||||
Long userId = Long.valueOf(params.get("id").toString());
|
|
||||||
User user = userMapper.findById(userId).orElse(null);
|
|
||||||
if (user == null) {
|
|
||||||
return Map.of("code", 404, "message", "用户不存在");
|
|
||||||
}
|
|
||||||
if (params.containsKey("name") && params.get("name") != null) {
|
|
||||||
user.setName(params.get("name").toString());
|
|
||||||
}
|
|
||||||
if (params.containsKey("phone") && params.get("phone") != null) {
|
|
||||||
String newPhone = params.get("phone").toString();
|
|
||||||
// 验证码校验
|
|
||||||
String code = params.get("code") != null ? params.get("code").toString() : "";
|
|
||||||
if (!"123456".equals(code)) {
|
|
||||||
return Map.of("code", 400, "message", "验证码错误");
|
|
||||||
}
|
|
||||||
// 检查手机号是否被占用
|
|
||||||
User existing = userMapper.findByPhone(newPhone);
|
|
||||||
if (existing != null && !existing.getId().equals(userId)) {
|
|
||||||
return Map.of("code", 400, "message", "手机号已被占用");
|
|
||||||
}
|
|
||||||
user.setPhone(newPhone);
|
|
||||||
}
|
|
||||||
if (params.containsKey("avatar") && params.get("avatar") != null) {
|
|
||||||
user.setAvatar(params.get("avatar").toString());
|
|
||||||
}
|
|
||||||
user.setUpdateTime(LocalDateTime.now());
|
|
||||||
userMapper.save(user);
|
|
||||||
user.setPassword(null);
|
|
||||||
return Map.of("code", 200, "message", "更新成功", "data", user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
spring:
|
|
||||||
application:
|
|
||||||
name: petstore-backend
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://192.144.152.238:3306/petstore?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true
|
|
||||||
username: root
|
|
||||||
password: Wabjtam123@
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
hikari:
|
|
||||||
max-lifetime: 1800000 # 30分钟,必须小于MySQL的wait_timeout(默认8小时)
|
|
||||||
connection-test-query: SELECT 1
|
|
||||||
validation-timeout: 3000
|
|
||||||
idle-timeout: 600000 # 10分钟
|
|
||||||
jpa:
|
|
||||||
hibernate:
|
|
||||||
ddl-auto: update
|
|
||||||
show-sql: true
|
|
||||||
properties:
|
|
||||||
hibernate:
|
|
||||||
dialect: org.hibernate.dialect.MySQLDialect
|
|
||||||
format_sql: true
|
|
||||||
servlet:
|
|
||||||
multipart:
|
|
||||||
max-file-size: 10MB
|
|
||||||
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
servlet:
|
|
||||||
context-path:
|
|
||||||
|
|
||||||
upload:
|
|
||||||
path: uploads
|
|
||||||
|
|
||||||
logging:
|
|
||||||
level:
|
|
||||||
com.petstore: debug
|
|
||||||
|
|
||||||
# 微信登录配置(需替换为实际值)
|
|
||||||
wechat:
|
|
||||||
appid: YOUR_APPID
|
|
||||||
appsecret: YOUR_APPSECRET
|
|
||||||
redirect_uri: http://localhost:8080/api/wechat/callback
|
|
||||||
Loading…
Reference in New Issue
Block a user