feat: adding account system and permission system

This commit is contained in:
Wzp-2008 2023-08-15 17:17:26 +08:00
parent d75d0114fb
commit 3299a08868
37 changed files with 1358 additions and 3 deletions

2
.gitignore vendored
View File

@ -35,4 +35,4 @@ out/
### VS Code ###
.vscode/
application.properties
run/

View File

@ -26,13 +26,23 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-web") {
exclude("org.springframework.boot", "spring-boot-starter-json")
}
implementation("org.springframework.boot:spring-boot-starter-websocket")
// https://mvnrepository.com/artifact/org.mybatis/mybatis
implementation("org.mybatis:mybatis:3.5.13")
implementation("com.baomidou:mybatis-plus-boot-starter:3.5.3.1") {
exclude("org.mybatis", "mybatis")
}
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
implementation("com.alibaba.fastjson2:fastjson2:2.0.38")
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2-extension
implementation("com.alibaba.fastjson2:fastjson2-extension:2.0.38")
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2-extension-spring6
implementation("com.alibaba.fastjson2:fastjson2-extension-spring6:2.0.38")
// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.mysql:mysql-connector-j")

View File

@ -1,11 +1,12 @@
package cn.wzpmc.commerce;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cn.wzpmc.commerce.mappers")
public class CommerceSystemBackendApplication {
public static void main(String[] args) {
SpringApplication.run(CommerceSystemBackendApplication.class, args);
}

View File

@ -0,0 +1,16 @@
package cn.wzpmc.commerce.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("**")
.allowedMethods("GET", "POST", "HEAD", "OPTION", "PUT")
.allowCredentials(true);
}
}

View File

@ -0,0 +1,32 @@
package cn.wzpmc.commerce.config;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.config.FastJsonConfig;
import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Configuration
public class FastJsonMvcConfiguration implements WebMvcConfigurer {
private static final boolean debug = false;
@Override
public void configureMessageConverters(@NonNull List<HttpMessageConverter<?>> converters) {
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
JSONWriter.Feature[] writerFeatures = {JSONWriter.Feature.MapSortField, JSONWriter.Feature.IgnoreNonFieldGetter, JSONWriter.Feature.PrettyFormat};
if (!debug) {
writerFeatures = new JSONWriter.Feature[]{JSONWriter.Feature.MapSortField, JSONWriter.Feature.IgnoreNonFieldGetter};
}
fastJsonConfig.setWriterFeatures(writerFeatures);
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
converter.setFastJsonConfig(fastJsonConfig);
converters.add(0, converter);
}
}

View File

@ -0,0 +1,79 @@
package cn.wzpmc.commerce.config;
import cn.wzpmc.commerce.controllers.AuthController;
import cn.wzpmc.commerce.utils.MessageResponse;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@SneakyThrows
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity security) {
security.authorizeHttpRequests(registry -> registry.requestMatchers("/api/auth/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/v3/**").permitAll()
.anyRequest().authenticated());
security.csrf(AbstractHttpConfigurer::disable);
security.cors(AbstractHttpConfigurer::disable);
security.formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer
.loginPage("/api/auth/notLogin")
.loginProcessingUrl("/api/auth/login")
.successHandler(AuthController::loginSuccess)
.failureHandler(AuthController::loginFailed));
security.logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer.logoutUrl("/api/auth/logout")
.logoutSuccessHandler(AuthController::logoutSuccess)
.permitAll());
security.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(new MyAuthenticationEntryPoint())
.accessDeniedHandler(new MyAccessDeniedHandler()));
return security.build();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
private static class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
String localizedMessage = authException.getLocalizedMessage();
response.setStatus(403);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new MessageResponse(localizedMessage, 403).copyToStream(outputStream);
outputStream.close();
}
}
private static class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
String localizedMessage = accessDeniedException.getLocalizedMessage();
response.setStatus(404);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new MessageResponse(localizedMessage, 403).copyToStream(outputStream);
outputStream.close();
}
}
}

View File

@ -0,0 +1,81 @@
package cn.wzpmc.commerce.controllers;
import cn.wzpmc.commerce.entities.User;
import cn.wzpmc.commerce.entities.vo.AccountVo;
import cn.wzpmc.commerce.services.UserService;
import cn.wzpmc.commerce.utils.DataResponse;
import cn.wzpmc.commerce.utils.MessageResponse;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;
@Tag(name = "AuthController", description = "用户权限、登录、注册控制器")
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final UserService service;
@Autowired
public AuthController(UserService service) {
this.service = service;
}
@SneakyThrows
public static void loginSuccess(HttpServletRequest ignoredRequest, HttpServletResponse response, Authentication authentication) {
response.setStatus(200);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new DataResponse<>((User) authentication.getPrincipal(), "登陆成功!").copyToStream(outputStream);
outputStream.close();
}
@SneakyThrows
public static void loginFailed(HttpServletRequest ignoredRequest, HttpServletResponse response, AuthenticationException authenticationException) {
response.setStatus(403);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new MessageResponse(authenticationException.getLocalizedMessage(), 403).copyToStream(outputStream);
outputStream.close();
}
@SneakyThrows
public static void logoutSuccess(HttpServletRequest ignoredRequest, HttpServletResponse response, Authentication ignoredAuthentication) {
response.setStatus(200);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new MessageResponse("登出成功!").copyToStream(outputStream);
outputStream.close();
}
@SneakyThrows
@Hidden
@GetMapping("/notLogin")
public void notLogin(HttpServletResponse response) {
response.setStatus(403);
response.setContentType("application/json");
ServletOutputStream outputStream = response.getOutputStream();
new MessageResponse("未登录", 403).copyToStream(outputStream);
outputStream.close();
}
@SneakyThrows
@Operation(summary = "注册", description = "注册一个账号")
@ApiResponse(responseCode = "200", description = "返回一个用户对象")
@PostMapping("/register")
public MessageResponse register(@RequestBody AccountVo accountVo) {
User user = service.addUser(accountVo);
if (user != null) {
return new DataResponse<>(user, "注册成功!");
}
return new MessageResponse("注册失败!用户已存在!", 304);
}
}

View File

@ -0,0 +1,78 @@
package cn.wzpmc.commerce.controllers;
import cn.wzpmc.commerce.entities.User;
import cn.wzpmc.commerce.entities.vo.PermissionVo;
import cn.wzpmc.commerce.services.PermissionService;
import cn.wzpmc.commerce.utils.DataResponse;
import cn.wzpmc.commerce.utils.MessageResponse;
import cn.wzpmc.commerce.utils.PageableResponse;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@Tag(name = "权限控制器", description = "权限管理器CRUD")
@RequestMapping("/authPermissions")
public class PermissionController {
private final PermissionService permissionService;
@Autowired
public PermissionController(PermissionService permissionService) {
this.permissionService = permissionService;
}
@Operation(summary = "添加一个权限")
@PostMapping(path = "/addAuthPermissions")
public MessageResponse addAuthPermissions(@RequestBody PermissionVo authPermissionVo, Authentication authentication) {
authPermissionVo.setCreateId(((User) authentication.getPrincipal()).getId());
if (permissionService.save(authPermissionVo)) {
return new MessageResponse("添加成功!");
}
return new MessageResponse("添加失败!", 500);
}
@Operation(summary = "修改权限")
@PostMapping(path = "/changeAuthPermissions")
public MessageResponse changeAuthPermissions(@RequestBody PermissionVo authPermissionVo) {
if (ServiceUtils.update(permissionService, authPermissionVo)) {
return new MessageResponse("修改成功!");
}
return new MessageResponse("修改失败!", 500);
}
@Operation(summary = "删除权限")
@GetMapping(path = "/delAuthPermissions")
public MessageResponse delAuthPermissions(@RequestParam("id") int id) {
if (permissionService.removeById(id)) {
return new MessageResponse("删除成功!");
}
return new MessageResponse("删除失败,未找到!", 500);
}
@Operation(summary = "分页获取权限")
@GetMapping(path = "/getAuthPermissions")
public PageableResponse<PermissionVo> getAuthPermissions(@RequestParam("page") int page, @RequestParam("num") int num) {
return PageableResponse.of(permissionService.page(new Page<>(page, num)));
}
@Operation(summary = "修改一个权限的状态")
@GetMapping(path = "/changeAuthPermissionsStats")
public MessageResponse changeAuthPermissionsStats(@RequestParam("authPermissionId") int id, @RequestParam("status") boolean status) {
if (ServiceUtils.updateStatus(this.permissionService, id, status)) {
return new MessageResponse("修改成功!");
}
return new MessageResponse("修改失败!", 500);
}
@Operation(summary = "获取所有权限")
@GetMapping(path = "/getAuthPermissionsId")
public DataResponse<List<PermissionVo>> getAuthPermissionsId() {
return new DataResponse<>(permissionService.list());
}
}

View File

@ -0,0 +1,59 @@
package cn.wzpmc.commerce.controllers;
import cn.wzpmc.commerce.entities.User;
import cn.wzpmc.commerce.entities.vo.AccountVo;
import cn.wzpmc.commerce.services.UserService;
import cn.wzpmc.commerce.utils.DataResponse;
import cn.wzpmc.commerce.utils.MessageResponse;
import cn.wzpmc.commerce.utils.PageableResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@Tag(name = "用户账号控制器", description = "用户的增删改查")
@RestController
@RequestMapping("/account")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@Operation(summary = "更改账户信息", description = "使用传入账号的id对账户信息进行更改包括用户名、邮箱、真名、权限组")
@PostMapping("/changeAccount")
public MessageResponse changeAccount(@RequestBody AccountVo accountVo) {
return this.userService.changeAccount(accountVo);
}
@Operation(summary = "修改密码", parameters = {@Parameter(name = "userId", description = "用户id"), @Parameter(name = "oldPassword", description = "旧密码"), @Parameter(name = "newPassword", description = "新密码")})
@PostMapping("/changePassword")
public MessageResponse changePassword(@RequestParam("userId") Integer userId, @RequestParam("oldPassword") String oldPassword, @RequestParam("newPassword") String newPassword) {
return this.userService.changePassword(userId, oldPassword, newPassword);
}
@Operation(summary = "删除用户", parameters = {@Parameter(name = "id", description = "用户id")})
@GetMapping("/delUser")
public MessageResponse delUser(@RequestParam("id") Integer id, Authentication authentication) {
if (id.equals(((User) authentication.getPrincipal()).getId())) {
return new MessageResponse("不能删除自己!", 403);
}
return this.userService.delUser(id);
}
@Operation(summary = "获取用户", parameters = {@Parameter(name = "page", description = "页数"), @Parameter(name = "num", description = "每页数量")})
@GetMapping("/getUser")
public PageableResponse<AccountVo> getUser(@RequestParam("page") Long page, @RequestParam("num") Long num, Authentication authentication) {
return this.userService.getUser(page, num, authentication);
}
@Operation(summary = "获取当前用户信息")
@GetMapping("/getUserInfo")
public DataResponse<User> getUserInfo(Authentication authentication) {
return new DataResponse<>((User) authentication.getPrincipal());
}
}

View File

@ -0,0 +1,23 @@
package cn.wzpmc.commerce.entities;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
@Data
public class Group {
private Integer id;
private String name;
private String description;
private List<Permission> permissions;
private int createId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

View File

@ -0,0 +1,34 @@
package cn.wzpmc.commerce.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@Data
public class MerchantUser extends User {
private Integer countDown;
private List<Order> orders;
private List<MerchantUserCommission> commissions;
public void calcOrderCommission() {
MerchantUserCommission[] array = commissions.toArray(new MerchantUserCommission[]{});
Arrays.sort(array, Comparator.comparingDouble(MerchantUserCommission::getPriceMin));
orderFor:
for (Order order : orders) {
double price = order.getPayAmt();
for (MerchantUserCommission commission : array) {
if (price >= commission.getPriceMin() && price <= commission.getPriceMax()) {
order.setCommission(price * commission.getCommission());
continue orderFor;
}
}
order.setCommission(0);
}
}
}

View File

@ -0,0 +1,21 @@
package cn.wzpmc.commerce.entities;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
public class MerchantUserCommission {
private int id;
private double priceMin;
private double priceMax;
private double commission;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

View File

@ -0,0 +1,36 @@
package cn.wzpmc.commerce.entities;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
public class Order {
private int id;
private String orderNo;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date orderTime;
private String orderStatus;
private String wangNo;
private String weChatNo;
private String alipayNo;
private double payAmt;
private int storeId;
private double commission;
private String alipayName;
private String rpName;
private String rpWeChatName;
private String remark;
private String phoneNumber;
private String cardNo;
private int flag;
private String flagRemark;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date payTime;
private String address;
private String sku;
}

View File

@ -0,0 +1,30 @@
package cn.wzpmc.commerce.entities;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.core.GrantedAuthority;
import java.util.Date;
@Data
public class Permission implements GrantedAuthority {
private Integer id;
private String name;
private String code;
private String description;
private int createId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@Override
@JSONField(serialize = false)
public String getAuthority() {
return this.code;
}
}

View File

@ -0,0 +1,51 @@
package cn.wzpmc.commerce.entities;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
@Data
public class User implements UserDetails {
private Integer id;
private String username;
private String email;
private String relName;
private String password;
private Group group;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.group.getPermissions();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,37 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("account")
public class AccountVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ServiceUtils.Updatable
private String username;
@ServiceUtils.Updatable
private String email;
@ServiceUtils.Updatable
@TableField("rel_name")
private String relName;
private String password;
@ServiceUtils.Updatable
@TableField("group_id")
private Integer groupId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,33 @@
package cn.wzpmc.commerce.entities.vo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("blacklist")
public class BlackListVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("store_id")
private Integer storeId;
@TableField("wang_no")
private String wangNo;
@TableField("wechat_no")
private String weChatNo;
@TableField("create_id")
private Integer createId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,41 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("commission")
public class CommissionVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ServiceUtils.Updatable
@TableField("range_min")
private Double range_min;
@ServiceUtils.Updatable
@TableField("range_max")
private Double range_max;
@ServiceUtils.Updatable
private Double base;
@ServiceUtils.Updatable
private Double extra;
@ServiceUtils.Updatable
@TableField("store_id")
private Integer storeId;
@TableField("create_id")
private Integer createId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,32 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("group")
public class GroupVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ServiceUtils.Updatable
private String name;
@ServiceUtils.Updatable
private String description;
@TableField("create_id")
private Integer createId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,51 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.enums.OrderStatus;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("order")
public class OrderVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String no;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date time;
private OrderStatus status;
@TableField("wang_no")
private String wangNo;
@TableField("wechat_no")
private String weChatNo;
@TableField("alipay_no")
private String alipayNo;
@TableField("alipay_name")
private String alipayName;
private String phone;
@TableField("card_no")
private String cardNo;
private Double payment;
@TableField("pay_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date payTime;
@TableField("head_name")
private String headName;
@TableField("head_wechat_name")
private String headWeChatName;
@TableField("store_id")
private Integer storeId;
private String remark;
private Integer flag;
@TableField("flag_remark")
private String flagRemark;
private String address;
private String sku;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,36 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("permissions")
public class PermissionVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ServiceUtils.Updatable
private String name;
@ServiceUtils.Updatable
private String code;
@ServiceUtils.Updatable
private String description;
@TableField("group_id")
private Integer groupId;
@TableField("create_id")
private Integer createId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,36 @@
package cn.wzpmc.commerce.entities.vo;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("store")
public class StoreVo {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ServiceUtils.Updatable
private String name;
@ServiceUtils.Updatable
@TableField("category_id")
private Integer categoryId;
@ServiceUtils.Updatable
@TableField("account_id")
private Integer accountId;
@TableField("create_id")
private Integer createId;
@TableField("create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@TableField(value = "update_time", update = "now()")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@TableLogic
private boolean del;
}

View File

@ -0,0 +1,6 @@
package cn.wzpmc.commerce.enums;
@SuppressWarnings("NonAsciiCharacters")
public enum OrderStatus {
商家驳回, 已完成, 已确认等待财务审核, 提交, 新建待提交数据, 财务审核已通过, 财务驳回
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.AccountVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AccountMapper extends BaseMapper<AccountVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.BlackListVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BlackListMapper extends BaseMapper<BlackListVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.CommissionVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CommissionMapper extends BaseMapper<CommissionVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.GroupVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface GroupMapper extends BaseMapper<GroupVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.OrderVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OrderMapper extends BaseMapper<OrderVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.PermissionVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PermissionsMapper extends BaseMapper<PermissionVo> {
}

View File

@ -0,0 +1,9 @@
package cn.wzpmc.commerce.mappers;
import cn.wzpmc.commerce.entities.vo.StoreVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface StoreMapper extends BaseMapper<StoreVo> {
}

View File

@ -0,0 +1,10 @@
package cn.wzpmc.commerce.services;
import cn.wzpmc.commerce.entities.vo.PermissionVo;
import cn.wzpmc.commerce.mappers.PermissionsMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class PermissionService extends ServiceImpl<PermissionsMapper, PermissionVo> {
}

View File

@ -0,0 +1,153 @@
package cn.wzpmc.commerce.services;
import cn.wzpmc.commerce.entities.Group;
import cn.wzpmc.commerce.entities.Permission;
import cn.wzpmc.commerce.entities.User;
import cn.wzpmc.commerce.entities.vo.AccountVo;
import cn.wzpmc.commerce.entities.vo.GroupVo;
import cn.wzpmc.commerce.entities.vo.PermissionVo;
import cn.wzpmc.commerce.mappers.AccountMapper;
import cn.wzpmc.commerce.mappers.GroupMapper;
import cn.wzpmc.commerce.mappers.PermissionsMapper;
import cn.wzpmc.commerce.utils.EntitiesUtils;
import cn.wzpmc.commerce.utils.MessageResponse;
import cn.wzpmc.commerce.utils.PageableResponse;
import cn.wzpmc.commerce.utils.ServiceUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserService extends ServiceImpl<AccountMapper, AccountVo> implements UserDetailsService {
private final AccountMapper accountMapper;
private final PermissionsMapper permissionsMapper;
private final GroupMapper groupMapper;
private final PasswordEncoder encoder;
private final Map<Integer, SecurityContext> userContexts = new HashMap<>();
@Autowired
public UserService(AccountMapper accountMapper, PermissionsMapper permissionsMapper, GroupMapper rolesMapper, PasswordEncoder encoder) {
this.accountMapper = accountMapper;
this.permissionsMapper = permissionsMapper;
this.groupMapper = rolesMapper;
this.encoder = encoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SecurityContext context = SecurityContextHolder.getContext();
User userByName = getUserByName(username);
userContexts.put(userByName.getId(), context);
return userByName;
}
public User addUser(AccountVo accountVo) {
try {
this.loadUserByUsername(accountVo.getUsername());
} catch (UsernameNotFoundException e) {
accountVo.setPassword(this.encoder.encode(accountVo.getPassword()));
accountVo.setCreateTime(new Date());
this.accountMapper.insert(accountVo);
return this.getUserByName(accountVo.getUsername());
}
return null;
}
private User getUserByName(String username) throws UsernameNotFoundException {
AccountVo accountVo = this.accountMapper.selectOne(new QueryWrapper<AccountVo>()
.eq("username", username)
);
if (accountVo == null) {
throw new UsernameNotFoundException("用户不存在");
}
return genUserFromAuthAccount(accountVo);
}
private User getUserById(int id) throws UsernameNotFoundException {
AccountVo accountVo = this.accountMapper.selectById(id);
if (accountVo == null) {
throw new UsernameNotFoundException("用户不存在");
}
return genUserFromAuthAccount(accountVo);
}
private User genUserFromAuthAccount(AccountVo accountVo) {
User user = EntitiesUtils.copyField(accountVo, new User());
GroupVo groupVo = groupMapper.selectById(accountVo.getGroupId());
Group group = EntitiesUtils.copyField(groupVo, new Group());
group.setPermissions(permissionsMapper.selectList(
new QueryWrapper<PermissionVo>().eq("group_id", groupVo.getId())
).stream().map(e -> EntitiesUtils.copyField(e, new Permission())).toList());
user.setGroup(group);
return user;
}
public MessageResponse changeAccount(AccountVo authAccountVo) {
String username = authAccountVo.getUsername();
Integer id = authAccountVo.getId();
try {
getUserByName(username);
} catch (UsernameNotFoundException e) {
if (!ServiceUtils.update(this, authAccountVo)) {
return new MessageResponse("修改失败!", 403);
}
this.updateUserAuthentication(id);
return new MessageResponse("修改成功");
}
return new MessageResponse("修改失败,用户名已存在", 403);
}
public MessageResponse changePassword(Integer userId, String oldPassword, String newPassword) {
oldPassword = this.encoder.encode(oldPassword);
newPassword = this.encoder.encode(newPassword);
if (this.accountMapper.selectOne(new QueryWrapper<AccountVo>().eq("id", userId).eq("password", oldPassword)) == null) {
return new MessageResponse("原密码错误", 403);
}
AccountVo accountVo = new AccountVo();
accountVo.setId(userId);
accountVo.setPassword(newPassword);
this.accountMapper.updateById(accountVo);
return new MessageResponse("修改成功");
}
public MessageResponse delUser(Integer id) {
if (this.accountMapper.deleteById(id) == 0) {
return new MessageResponse("未找到用户", 404);
}
return new MessageResponse("修改成功");
}
public PageableResponse<AccountVo> getUser(Long page, Long num, Authentication authentication) {
User user = (User) authentication.getPrincipal();
Integer uid = user.getId();
Integer id = user.getGroup().getId();
QueryWrapper<AccountVo> queryWrapper = new QueryWrapper<AccountVo>().gt("role_id", id).or().eq("id", uid);
Page<AccountVo> users = this.accountMapper.selectPage(new Page<>(page, num), queryWrapper);
Long l = this.accountMapper.selectCount(queryWrapper);
return new PageableResponse<>(users.getRecords(), l);
}
public void updateUserAuthentication(int id) {
if (!userContexts.containsKey(id)) {
return;
}
User userById = getUserById(id);
Authentication newAuth = new UsernamePasswordAuthenticationToken(userById, null, userById.getAuthorities());
SecurityContext securityContext = userContexts.get(id);
securityContext.setAuthentication(newAuth);
}
}

View File

@ -0,0 +1,31 @@
package cn.wzpmc.commerce.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
public class DataResponse<T> extends MessageResponse {
private T data;
public DataResponse(T data, int code, String message) {
super(message, code);
this.data = data;
}
public DataResponse(T data, String message) {
super(message);
this.data = data;
}
public DataResponse() {
super();
this.data = null;
}
public String toString() {
return super.toString();
}
}

View File

@ -0,0 +1,103 @@
package cn.wzpmc.commerce.utils;
import cn.wzpmc.commerce.entities.User;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class EntitiesUtils {
@SneakyThrows
public static <F, T> T copyField(F from, T to, NameParser parser) {
Class<?> fromClass = from.getClass();
Class<?> toClass = to.getClass();
Method[] fromMethods = fromClass.getDeclaredMethods();
Method[] toMethods = toClass.getDeclaredMethods();
Map<String, Method> getters = new HashMap<>();
for (Method fromMethod : fromMethods) {
String methodName = fromMethod.getName();
if ((methodName.startsWith("get") || methodName.startsWith("is")) && fromMethod.getParameters().length == 0) {
getters.put(parser.getterParse(methodName), fromMethod);
}
}
for (Method toMethod : toMethods) {
String methodName = toMethod.getName();
Parameter[] parameters = toMethod.getParameters();
if (methodName.startsWith("set") && parameters.length == 1) {
String fieldName = parser.setterParse(methodName);
if (getters.containsKey(fieldName)) {
Method getter = getters.get(fieldName);
Class<?> returnType = getter.getReturnType();
if (!parameters[0].getType().equals(returnType)) {
continue;
}
Object fieldValue = getter.invoke(from);
toMethod.invoke(to, fieldValue);
}
}
}
return to;
}
public static <F, T> T copyField(F from, T to) {
return copyField(from, to, new NameParser());
}
@SneakyThrows
public static <T> void addCreateId(T obj) {
Class<?> aClass = obj.getClass();
int userId = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId();
for (Field declaredField : aClass.getDeclaredFields()) {
declaredField.setAccessible(true);
if (declaredField.getName().equals("create_id")) {
declaredField.set(obj, userId);
return;
}
}
log.warn("在类{}中未找到create_id", aClass);
}
@NoArgsConstructor
public static class NameParser {
private final HashMap<String, String> getterParsers = new HashMap<>();
private final HashMap<String, String> setterParsers = new HashMap<>();
public NameParser addParser(String from, String to) {
this.getterParsers.put(from, to);
this.setterParsers.put(from, to);
return this;
}
public NameParser addGetter(String from, String to) {
this.getterParsers.put(from, to);
return this;
}
public NameParser addSetter(String from, String to) {
this.setterParsers.put(from, to);
return this;
}
private String getterParse(String from) {
if (from.startsWith("get")) {
from = from.replaceFirst("get", "");
} else {
from = from.replaceFirst("is", "");
}
return getterParsers.getOrDefault(from, from);
}
private String setterParse(String from) {
from = from.replaceFirst("set", "");
return setterParsers.getOrDefault(from, from);
}
}
}

View File

@ -0,0 +1,42 @@
package cn.wzpmc.commerce.utils;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
@Data
@AllArgsConstructor
public class MessageResponse {
private String msg;
private int code;
private long timestamp;
public MessageResponse() {
this.code = 200;
this.msg = "成功";
this.timestamp = new Date().getTime();
}
public MessageResponse(String msg) {
this(msg, 200);
}
public MessageResponse(String msg, int code) {
this(msg, code, new Date().getTime());
}
public String toString() {
return JSONObject.from(this).toJSONString(JSONWriter.Feature.IgnoreNonFieldGetter);
}
@SneakyThrows
public void copyToStream(OutputStream stream) {
stream.write(this.toString().getBytes(StandardCharsets.UTF_8));
}
}

View File

@ -0,0 +1,35 @@
package cn.wzpmc.commerce.utils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class PageableResponse<T> extends DataResponse<PageableResponse.PageableData<T>> {
public PageableResponse(List<T> data, long total) {
this(new PageableData<>(data, total));
}
public PageableResponse(PageableData<T> data) {
super(data);
}
public static <T> PageableResponse<T> of(Page<T> pageResult) {
return new PageableResponse<>(PageableData.of(pageResult));
}
@Data
@AllArgsConstructor
public static class PageableData<T> {
private List<T> info;
private long total;
public static <T> PageableData<T> of(Page<T> pageResult) {
return new PageableData<>(pageResult.getRecords(), pageResult.getTotal());
}
}
}

View File

@ -0,0 +1,95 @@
package cn.wzpmc.commerce.utils;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Objects;
@Slf4j
public class ServiceUtils {
@SneakyThrows
public static <T> boolean update(IService<T> service, T entities) {
Class<T> entityClass = service.getEntityClass();
Field[] declaredFields = entityClass.getDeclaredFields();
UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
Object id = null;
String idName = "id";
for (Field field : declaredFields) {
field.setAccessible(true);
String fieldName = field.getName();
TableId tableId = field.getDeclaredAnnotation(TableId.class);
if (Objects.nonNull(tableId) || fieldName.equals("id")) {
id = field.get(entities);
if (Objects.nonNull(tableId)) {
idName = tableId.value();
}
}
Updatable declaredAnnotation = field.getDeclaredAnnotation(Updatable.class);
if (declaredAnnotation != null) {
TableField tableField = field.getDeclaredAnnotation(TableField.class);
String name;
if (tableField == null) {
name = fieldName;
} else {
name = tableField.value();
}
Object value = field.get(entities);
updateWrapper.set(Objects.nonNull(value), name, value);
}
}
if (id == null) {
log.warn("类{}未找到id", entityClass);
return false;
}
updateWrapper.eq(idName, id);
return service.update(updateWrapper);
}
public static <T> boolean updateStatus(IService<T> service, int id, boolean newStatus) {
Class<T> entityClass = service.getEntityClass();
Field[] declaredFields = entityClass.getDeclaredFields();
UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
boolean has = false;
for (Field field : declaredFields) {
field.setAccessible(true);
String fieldName = field.getName();
Status declaredAnnotation = field.getDeclaredAnnotation(Status.class);
if (declaredAnnotation != null) {
TableField tableField = field.getDeclaredAnnotation(TableField.class);
String name;
if (tableField == null) {
name = fieldName;
} else {
name = tableField.value();
}
has = true;
updateWrapper.set(name, newStatus);
}
}
if (!has) {
log.warn("类{}未找到status注解", entityClass);
return false;
}
updateWrapper.eq("id", id);
return service.update(updateWrapper);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface Updatable {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface Status {
}
}