暂存提交

This commit is contained in:
2026-07-31 14:57:09 +08:00
parent 7a19e2f096
commit f8804fbc52
53 changed files with 3771 additions and 0 deletions
@@ -0,0 +1,114 @@
package com.baiyue.warehouse.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanySaveRequest;
import com.baiyue.warehouse.dto.MasterDataQueries.StatusRequest;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.service.ExpressCompanyQueryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/** 平台快递公司管理接口。 */
@Validated
@Tag(name = "平台快递公司", description = "按目标租户分页查询快递公司主数据")
@RestController
@RequestMapping("/api/platform/v1/express-companies")
public class PlatformExpressCompanyController {
private final ExpressCompanyQueryService service;
public PlatformExpressCompanyController(ExpressCompanyQueryService service) {
this.service = service;
}
@GetMapping
@PreAuthorize("hasAuthority('platform:express:list')")
@Operation(summary = "分页查询快递公司", description = "按目标租户、关键字和状态分页查询快递公司")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "查询条件无效"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "需要登录"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "需要平台快递公司查询权限")
})
public ApiResponse<PageResult<ExpressCompanyEntity>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@Valid @ParameterObject ExpressCompanyPageQuery query) {
requirePlatform(user);
return ApiResponse.success(service.page(tenantId, query));
}
@GetMapping("/{id}")
@PreAuthorize("hasAuthority('platform:express:list')")
@Operation(summary = "查询快递公司详情", description = "查询目标租户内的快递公司详情")
public ApiResponse<ExpressCompanyEntity> detail(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id) {
requirePlatform(user);
return ApiResponse.success(service.detail(tenantId, id));
}
@PostMapping
@PreAuthorize("hasAuthority('platform:express:create')")
@Operation(summary = "新增快递公司", description = "在目标租户内新增快递公司")
public ApiResponse<Long> create(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@Valid @RequestBody ExpressCompanySaveRequest request) {
requirePlatform(user);
return ApiResponse.success(service.create(tenantId, request));
}
@PutMapping("/{id}")
@PreAuthorize("hasAuthority('platform:express:edit')")
@Operation(summary = "编辑快递公司", description = "编辑目标租户内的快递公司")
public ApiResponse<Void> update(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id,
@Valid @RequestBody ExpressCompanySaveRequest request) {
requirePlatform(user);
service.update(tenantId, id, request);
return ApiResponse.success(null);
}
@PatchMapping("/{id}/status")
@PreAuthorize("hasAuthority('platform:express:status')")
@Operation(summary = "启用或停用快递公司", description = "通过状态变更停用主数据,不物理删除历史记录")
public ApiResponse<Void> changeStatus(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id,
@Valid @RequestBody StatusRequest request) {
requirePlatform(user);
service.changeStatus(tenantId, id, request.status());
return ApiResponse.success(null);
}
private static void requirePlatform(AuthenticatedUser user) {
if (user == null || !"PLATFORM".equals(user.appCode())) {
throw new BizException(ResultCode.FORBIDDEN, "仅允许平台端访问");
}
}
}
@@ -0,0 +1,114 @@
package com.baiyue.warehouse.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerSaveRequest;
import com.baiyue.warehouse.dto.MasterDataQueries.StatusRequest;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.service.PlatformPartnerQueryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/** 平台往来单位管理接口。 */
@Validated
@Tag(name = "平台往来单位", description = "按目标租户分页查询往来单位主数据")
@RestController
@RequestMapping("/api/platform/v1/partners")
public class PlatformPartnerController {
private final PlatformPartnerQueryService service;
public PlatformPartnerController(PlatformPartnerQueryService service) {
this.service = service;
}
@GetMapping
@PreAuthorize("hasAuthority('platform:counterparty:list')")
@Operation(summary = "分页查询往来单位", description = "按目标租户、关键字、类型和状态分页查询往来单位")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "查询条件无效"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "需要登录"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "需要平台往来单位查询权限")
})
public ApiResponse<PageResult<PartnerEntity>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@Valid @ParameterObject PartnerPageQuery query) {
requirePlatform(user);
return ApiResponse.success(service.page(tenantId, query));
}
@GetMapping("/{id}")
@PreAuthorize("hasAuthority('platform:counterparty:list')")
@Operation(summary = "查询往来单位详情", description = "查询目标租户内的往来单位详情")
public ApiResponse<PartnerEntity> detail(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id) {
requirePlatform(user);
return ApiResponse.success(service.detail(tenantId, id));
}
@PostMapping
@PreAuthorize("hasAuthority('platform:counterparty:create')")
@Operation(summary = "新增往来单位", description = "在目标租户内新增往来单位")
public ApiResponse<Long> create(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@Valid @RequestBody PartnerSaveRequest request) {
requirePlatform(user);
return ApiResponse.success(service.create(tenantId, request));
}
@PutMapping("/{id}")
@PreAuthorize("hasAuthority('platform:counterparty:edit')")
@Operation(summary = "编辑往来单位", description = "编辑目标租户内的往来单位")
public ApiResponse<Void> update(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id,
@Valid @RequestBody PartnerSaveRequest request) {
requirePlatform(user);
service.update(tenantId, id, request);
return ApiResponse.success(null);
}
@PatchMapping("/{id}/status")
@PreAuthorize("hasAuthority('platform:counterparty:status')")
@Operation(summary = "启用或停用往来单位", description = "通过状态变更停用主数据,不物理删除历史记录")
public ApiResponse<Void> changeStatus(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @Min(1) long tenantId,
@PathVariable long id,
@Valid @RequestBody StatusRequest request) {
requirePlatform(user);
service.changeStatus(tenantId, id, request.status());
return ApiResponse.success(null);
}
private static void requirePlatform(AuthenticatedUser user) {
if (user == null || !"PLATFORM".equals(user.appCode())) {
throw new BizException(ResultCode.FORBIDDEN, "仅允许平台端访问");
}
}
}
@@ -0,0 +1,116 @@
package com.baiyue.warehouse.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
/** 平台基础资料请求契约。 */
public final class MasterDataQueries {
private MasterDataQueries() {
}
@Schema(name = "PartnerPageQuery", description = "平台往来单位分页查询条件")
public record PartnerPageQuery(
@Schema(description = "页码,从 1 开始", example = "1", defaultValue = "1")
@Min(1) long page,
@Schema(description = "每页条数", example = "20", defaultValue = "20")
@Min(1) @Max(200) long size,
@Schema(description = "编码、名称、联系人或联系电话关键字", example = "示例客户")
@Size(max = 128) String keyword,
@Schema(description = "往来单位类型", allowableValues = {"CUSTOMER", "SUPPLIER", "BOTH"})
@Pattern(regexp = "^(CUSTOMER|SUPPLIER|BOTH)$") String type,
@Schema(description = "状态", allowableValues = {"ACTIVE", "INACTIVE"})
@Pattern(regexp = "^(ACTIVE|INACTIVE)$") String status
) {
public PartnerPageQuery {
if (page == 0) {
page = 1;
}
if (size == 0) {
size = 20;
}
}
}
@Schema(name = "ExpressCompanyPageQuery", description = "平台快递公司分页查询条件")
public record ExpressCompanyPageQuery(
@Schema(description = "页码,从 1 开始", example = "1", defaultValue = "1")
@Min(1) long page,
@Schema(description = "每页条数", example = "20", defaultValue = "20")
@Min(1) @Max(200) long size,
@Schema(description = "快递公司编码或名称关键字", example = "顺丰")
@Size(max = 128) String keyword,
@Schema(description = "状态", allowableValues = {"ACTIVE", "INACTIVE"})
@Pattern(regexp = "^(ACTIVE|INACTIVE)$") String status
) {
public ExpressCompanyPageQuery {
if (page == 0) {
page = 1;
}
if (size == 0) {
size = 20;
}
}
}
@Schema(name = "PartnerSaveRequest", description = "平台往来单位保存请求")
public record PartnerSaveRequest(
@Schema(description = "租户内唯一往来单位编码", example = "CUS-001")
@NotBlank @Size(max = 50) String partnerCode,
@Schema(description = "往来单位名称", example = "深圳示例贸易有限公司")
@NotBlank @Size(max = 128) String partnerName,
@Schema(description = "往来单位类型", allowableValues = {"CUSTOMER", "SUPPLIER", "BOTH"})
@NotBlank @Pattern(regexp = "^(CUSTOMER|SUPPLIER|BOTH)$") String partnerType,
@Schema(description = "联系人", example = "张三")
@NotBlank @Size(max = 64) String contactName,
@Schema(description = "联系电话", example = "13800138000")
@NotBlank @Size(max = 32) String contactPhone,
@Schema(description = "联系邮箱", example = "contact@example.com")
@Email @Size(max = 128) String contactEmail,
@Schema(description = "国家二字码", example = "CN")
@NotBlank @Pattern(regexp = "^[A-Z]{2}$") String countryCode,
@Schema(description = "省或州", example = "广东省")
@Size(max = 64) String province,
@Schema(description = "城市", example = "深圳市")
@NotBlank @Size(max = 64) String city,
@Schema(description = "区县", example = "宝安区")
@Size(max = 64) String district,
@Schema(description = "详细地址", example = "航城街道示例路 1 号")
@NotBlank @Size(max = 255) String addressLine,
@Schema(description = "邮政编码", example = "518000")
@Size(max = 20) String postalCode,
@Schema(description = "状态", allowableValues = {"ACTIVE", "INACTIVE"})
@NotBlank @Pattern(regexp = "^(ACTIVE|INACTIVE)$") String status
) {
}
@Schema(name = "ExpressCompanySaveRequest", description = "平台快递公司保存请求")
public record ExpressCompanySaveRequest(
@Schema(description = "租户内唯一快递公司编码", example = "SF")
@NotBlank @Size(max = 50) String companyCode,
@Schema(description = "快递公司名称", example = "顺丰速运")
@NotBlank @Size(max = 128) String companyName,
@Schema(description = "客服电话", example = "95338")
@Size(max = 32) String servicePhone,
@Schema(description = "官方网站", example = "https://www.sf-express.com")
@Size(max = 255) String website,
@Schema(description = "轨迹查询地址模板,可包含 {trackingNo}")
@Size(max = 500) String trackingUrlTemplate,
@Schema(description = "状态", allowableValues = {"ACTIVE", "INACTIVE"})
@NotBlank @Pattern(regexp = "^(ACTIVE|INACTIVE)$") String status,
@Schema(description = "备注")
@Size(max = 500) String remark
) {
}
@Schema(name = "MasterDataStatusRequest", description = "基础资料状态变更请求")
public record StatusRequest(
@Schema(description = "目标状态", allowableValues = {"ACTIVE", "INACTIVE"})
@NotBlank @Pattern(regexp = "^(ACTIVE|INACTIVE)$") String status
) {
}
}
@@ -0,0 +1,36 @@
package com.baiyue.warehouse.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baiyue.common.core.model.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/** 租户维度快递公司主数据。 */
@Schema(name = "ExpressCompany", description = "租户维度快递公司主数据")
@Getter
@Setter
@TableName("wh_express_company")
public class ExpressCompanyEntity extends BaseEntity {
@Schema(description = "快递公司记录 ID", example = "192000000000000001")
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "所属租户 ID", example = "10001")
private Long tenantId;
@Schema(description = "租户内唯一快递公司编码", example = "SF")
private String companyCode;
@Schema(description = "快递公司名称", example = "顺丰速运")
private String companyName;
@Schema(description = "客服电话", example = "95338")
private String servicePhone;
@Schema(description = "官方网站", example = "https://www.sf-express.com")
private String website;
@Schema(description = "轨迹查询地址模板,可包含 {trackingNo}")
private String trackingUrlTemplate;
@Schema(description = "状态", example = "ACTIVE", allowableValues = {"ACTIVE", "INACTIVE"})
private String status;
@Schema(description = "备注")
private String remark;
}
@@ -0,0 +1,8 @@
package com.baiyue.warehouse.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
/** 快递公司持久化映射。 */
public interface ExpressCompanyMapper extends BaseMapper<ExpressCompanyEntity> {
}
@@ -0,0 +1,54 @@
package com.baiyue.warehouse.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.mapper.ExpressCompanyMapper;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
/** 快递公司主数据仓储。 */
@Repository
public class ExpressCompanyRepository {
private final ExpressCompanyMapper mapper;
public ExpressCompanyRepository(ExpressCompanyMapper mapper) {
this.mapper = mapper;
}
public PageResult<ExpressCompanyEntity> page(long tenantId, ExpressCompanyPageQuery query) {
LambdaQueryWrapper<ExpressCompanyEntity> wrapper = new LambdaQueryWrapper<ExpressCompanyEntity>()
.eq(ExpressCompanyEntity::getTenantId, tenantId)
.eq(StringUtils.hasText(query.status()), ExpressCompanyEntity::getStatus, query.status())
.and(StringUtils.hasText(query.keyword()), condition -> condition
.like(ExpressCompanyEntity::getCompanyCode, query.keyword())
.or()
.like(ExpressCompanyEntity::getCompanyName, query.keyword()))
.orderByAsc(ExpressCompanyEntity::getCompanyName)
.orderByAsc(ExpressCompanyEntity::getId);
return PageResult.from(mapper.selectPage(new Page<>(query.page(), query.size()), wrapper));
}
public ExpressCompanyEntity findOwned(long tenantId, long id) {
return mapper.selectOne(new LambdaQueryWrapper<ExpressCompanyEntity>()
.eq(ExpressCompanyEntity::getTenantId, tenantId)
.eq(ExpressCompanyEntity::getId, id));
}
public boolean existsCode(long tenantId, String code, Long excludedId) {
return mapper.selectCount(new LambdaQueryWrapper<ExpressCompanyEntity>()
.eq(ExpressCompanyEntity::getTenantId, tenantId)
.eq(ExpressCompanyEntity::getCompanyCode, code)
.ne(excludedId != null, ExpressCompanyEntity::getId, excludedId)) > 0;
}
public void insert(ExpressCompanyEntity entity) {
mapper.insert(entity);
}
public void save(ExpressCompanyEntity entity) {
mapper.updateById(entity);
}
}
@@ -0,0 +1,98 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.audit.BusinessAudit;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanySaveRequest;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import org.springframework.stereotype.Service;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
/** 平台快递公司管理服务。 */
@Service
public class ExpressCompanyQueryService {
private final ExpressCompanyRepository repository;
public ExpressCompanyQueryService(ExpressCompanyRepository repository) {
this.repository = repository;
}
public PageResult<ExpressCompanyEntity> page(long tenantId, ExpressCompanyPageQuery query) {
return repository.page(tenantId, query);
}
public ExpressCompanyEntity detail(long tenantId, long id) {
return owned(tenantId, id);
}
@Transactional
@BusinessAudit(event = "EXPRESS_COMPANY_CREATE", resourceType = "EXPRESS_COMPANY", description = "新增快递公司", captureBefore = false)
public Long create(long tenantId, ExpressCompanySaveRequest request) {
ensureCodeUnique(tenantId, request.companyCode(), null);
ExpressCompanyEntity entity = new ExpressCompanyEntity();
entity.setTenantId(tenantId);
copy(entity, request);
try {
repository.insert(entity);
} catch (DataIntegrityViolationException exception) {
throw new BizException(ResultCode.CONFLICT, "快递公司编码已存在");
}
return entity.getId();
}
@Transactional
@BusinessAudit(event = "EXPRESS_COMPANY_UPDATE", resourceType = "EXPRESS_COMPANY", resourceId = "#id", description = "编辑快递公司")
public void update(long tenantId, long id, ExpressCompanySaveRequest request) {
ExpressCompanyEntity entity = owned(tenantId, id);
ensureCodeUnique(tenantId, request.companyCode(), id);
copy(entity, request);
try {
repository.save(entity);
} catch (DataIntegrityViolationException exception) {
throw new BizException(ResultCode.CONFLICT, "快递公司编码已存在");
}
}
@Transactional
@BusinessAudit(event = "EXPRESS_COMPANY_STATUS_CHANGE", resourceType = "EXPRESS_COMPANY", resourceId = "#id", description = "启停快递公司")
public void changeStatus(long tenantId, long id, String status) {
requireStatus(status);
ExpressCompanyEntity entity = owned(tenantId, id);
entity.setStatus(status);
repository.save(entity);
}
private ExpressCompanyEntity owned(long tenantId, long id) {
ExpressCompanyEntity entity = repository.findOwned(tenantId, id);
if (entity == null) {
throw new BizException(ResultCode.NOT_FOUND, "快递公司不存在");
}
return entity;
}
private void ensureCodeUnique(long tenantId, String code, Long excludedId) {
if (repository.existsCode(tenantId, code, excludedId)) {
throw new BizException(ResultCode.CONFLICT, "快递公司编码已存在");
}
}
private static void requireStatus(String status) {
if (!"ACTIVE".equals(status) && !"INACTIVE".equals(status)) {
throw new BizException(ResultCode.BAD_REQUEST, "状态只能设置为 ACTIVE 或 INACTIVE");
}
}
private static void copy(ExpressCompanyEntity entity, ExpressCompanySaveRequest request) {
entity.setCompanyCode(request.companyCode());
entity.setCompanyName(request.companyName());
entity.setServicePhone(request.servicePhone());
entity.setWebsite(request.website());
entity.setTrackingUrlTemplate(request.trackingUrlTemplate());
entity.setStatus(request.status());
entity.setRemark(request.remark());
}
}
@@ -0,0 +1,104 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.audit.BusinessAudit;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerSaveRequest;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.PartnerRepository;
import org.springframework.stereotype.Service;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
/** 平台往来单位管理服务。 */
@Service
public class PlatformPartnerQueryService {
private final PartnerRepository repository;
public PlatformPartnerQueryService(PartnerRepository repository) {
this.repository = repository;
}
public PageResult<PartnerEntity> page(long tenantId, PartnerPageQuery query) {
return repository.page(tenantId, query);
}
public PartnerEntity detail(long tenantId, long id) {
return owned(tenantId, id);
}
@Transactional
@BusinessAudit(event = "PARTNER_CREATE", resourceType = "PARTNER", description = "新增往来单位", captureBefore = false)
public Long create(long tenantId, PartnerSaveRequest request) {
ensureCodeUnique(tenantId, request.partnerCode(), null);
PartnerEntity entity = new PartnerEntity();
entity.setTenantId(tenantId);
copy(entity, request);
try {
repository.insert(entity);
} catch (DataIntegrityViolationException exception) {
throw new BizException(ResultCode.CONFLICT, "往来单位编码已存在");
}
return entity.getId();
}
@Transactional
@BusinessAudit(event = "PARTNER_UPDATE", resourceType = "PARTNER", resourceId = "#id", description = "编辑往来单位")
public void update(long tenantId, long id, PartnerSaveRequest request) {
PartnerEntity entity = owned(tenantId, id);
ensureCodeUnique(tenantId, request.partnerCode(), id);
copy(entity, request);
try {
repository.save(entity);
} catch (DataIntegrityViolationException exception) {
throw new BizException(ResultCode.CONFLICT, "往来单位编码已存在");
}
}
@Transactional
@BusinessAudit(event = "PARTNER_STATUS_CHANGE", resourceType = "PARTNER", resourceId = "#id", description = "启停往来单位")
public void changeStatus(long tenantId, long id, String status) {
requireStatus(status);
PartnerEntity entity = owned(tenantId, id);
entity.setStatus(status);
repository.save(entity);
}
private PartnerEntity owned(long tenantId, long id) {
PartnerEntity entity = repository.findOwned(tenantId, id);
if (entity == null) {
throw new BizException(ResultCode.NOT_FOUND, "往来单位不存在");
}
return entity;
}
private void ensureCodeUnique(long tenantId, String code, Long excludedId) {
if (repository.existsCode(tenantId, code, excludedId)) {
throw new BizException(ResultCode.CONFLICT, "往来单位编码已存在");
}
}
private static void requireStatus(String status) {
if (!"ACTIVE".equals(status) && !"INACTIVE".equals(status)) {
throw new BizException(ResultCode.BAD_REQUEST, "状态只能设置为 ACTIVE 或 INACTIVE");
}
}
private static void copy(PartnerEntity entity, PartnerSaveRequest request) {
entity.setPartnerCode(request.partnerCode());
entity.setPartnerName(request.partnerName());
entity.setPartnerType(request.partnerType());
entity.setContactName(request.contactName());
entity.setContactPhone(request.contactPhone());
entity.setContactEmail(request.contactEmail());
entity.setCountryCode(request.countryCode());
entity.setProvince(request.province());
entity.setCity(request.city());
entity.setDistrict(request.district());
entity.setAddressLine(request.addressLine());
entity.setPostalCode(request.postalCode());
entity.setStatus(request.status());
}
}
@@ -0,0 +1,23 @@
SET NAMES utf8mb4;
USE baiyue_warehouse;
CREATE TABLE IF NOT EXISTS wh_express_company (
id BIGINT UNSIGNED NOT NULL COMMENT '快递公司记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '所属租户ID',
company_code VARCHAR(50) NOT NULL COMMENT '租户内唯一快递公司编码',
company_name VARCHAR(128) NOT NULL COMMENT '快递公司名称',
service_phone VARCHAR(32) DEFAULT NULL COMMENT '客服电话',
website VARCHAR(255) DEFAULT NULL COMMENT '官方网站',
tracking_url_template VARCHAR(500) DEFAULT NULL COMMENT '轨迹查询地址模板,可包含{trackingNo}',
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '快递公司状态',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
created_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED DEFAULT NULL COMMENT '最后修改人ID',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_wh_express_tenant_code (tenant_id, company_code, deleted),
KEY idx_wh_express_tenant_status (tenant_id, status, deleted)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户维度快递公司主数据';