推广表,admin后台更新

This commit is contained in:
aiShuiJiaoDeXioShou 2024-09-12 18:51:07 +08:00
parent a169712e48
commit da8359540f
45 changed files with 2672 additions and 57 deletions

View File

@ -57,4 +57,8 @@ public interface ErrorCodeConstants {
ErrorCode PET_EXPERT_CERTIFICATION_NOT_EXISTS = new ErrorCode(1_008_009_001, "宠托师审核不存在");
ErrorCode NOTIFICATIONS_NOT_EXISTS = new ErrorCode(1_008_009_002, "通知不存在");
ErrorCode AD_NOT_EXISTS = new ErrorCode(1_008_009_003, "推广不存在");
}

View File

@ -0,0 +1,95 @@
package cn.yskj.linghe.module.acdr.controller.admin.ad;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.CommonResult;
import cn.yskj.linghe.framework.common.util.object.BeanUtils;
import static cn.yskj.linghe.framework.common.pojo.CommonResult.success;
import cn.yskj.linghe.framework.excel.core.util.ExcelUtils;
import cn.yskj.linghe.framework.apilog.core.annotation.ApiAccessLog;
import static cn.yskj.linghe.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.yskj.linghe.module.acdr.controller.admin.ad.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.ad.AdDO;
import cn.yskj.linghe.module.acdr.service.ad.AdService;
@Tag(name = "管理后台 - 推广")
@RestController
@RequestMapping("/acdr/ad")
@Validated
public class AdController {
@Resource
private AdService adService;
@PostMapping("/create")
@Operation(summary = "创建推广")
@PreAuthorize("@ss.hasPermission('acdr:ad:create')")
public CommonResult<Long> createAd(@Valid @RequestBody AdSaveReqVO createReqVO) {
return success(adService.createAd(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新推广")
@PreAuthorize("@ss.hasPermission('acdr:ad:update')")
public CommonResult<Boolean> updateAd(@Valid @RequestBody AdSaveReqVO updateReqVO) {
adService.updateAd(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除推广")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('acdr:ad:delete')")
public CommonResult<Boolean> deleteAd(@RequestParam("id") Long id) {
adService.deleteAd(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得推广")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('acdr:ad:query')")
public CommonResult<AdRespVO> getAd(@RequestParam("id") Long id) {
AdDO ad = adService.getAd(id);
return success(BeanUtils.toBean(ad, AdRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得推广分页")
@PreAuthorize("@ss.hasPermission('acdr:ad:query')")
public CommonResult<PageResult<AdRespVO>> getAdPage(@Valid AdPageReqVO pageReqVO) {
PageResult<AdDO> pageResult = adService.getAdPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AdRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出推广 Excel")
@PreAuthorize("@ss.hasPermission('acdr:ad:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportAdExcel(@Valid AdPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<AdDO> list = adService.getAdPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "推广.xls", "数据", AdRespVO.class,
BeanUtils.toBean(list, AdRespVO.class));
}
}

View File

@ -0,0 +1,34 @@
package cn.yskj.linghe.module.acdr.controller.admin.ad.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.yskj.linghe.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 推广分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AdPageReqVO extends PageParam {
@Schema(description = "资源类型", example = "2")
private Integer type;
@Schema(description = "图片路径", example = "https://www.iocoder.cn")
private String imageUrl;
@Schema(description = "推广语")
private String content;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "资源路径", example = "https://www.iocoder.cn")
private String pagesUrl;
}

View File

@ -0,0 +1,42 @@
package cn.yskj.linghe.module.acdr.controller.admin.ad.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.yskj.linghe.framework.excel.core.annotations.DictFormat;
import cn.yskj.linghe.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 推广 Response VO")
@Data
@ExcelIgnoreUnannotated
public class AdRespVO {
@Schema(description = "推广ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18022")
@ExcelProperty("推广ID")
private Long id;
@Schema(description = "资源类型", example = "2")
@ExcelProperty(value = "资源类型", converter = DictConvert.class)
@DictFormat("promotion_type") // TODO 代码优化建议设置到对应的 DictTypeConstants 枚举类中
private Integer type;
@Schema(description = "图片路径", example = "https://www.iocoder.cn")
@ExcelProperty("图片路径")
private String imageUrl;
@Schema(description = "推广语")
@ExcelProperty("推广语")
private String content;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "资源路径", example = "https://www.iocoder.cn")
@ExcelProperty("资源路径")
private String pagesUrl;
}

View File

@ -0,0 +1,27 @@
package cn.yskj.linghe.module.acdr.controller.admin.ad.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 推广新增/修改 Request VO")
@Data
public class AdSaveReqVO {
@Schema(description = "推广ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18022")
private Long id;
@Schema(description = "资源类型", example = "2")
private Integer type;
@Schema(description = "图片路径", example = "https://www.iocoder.cn")
private String imageUrl;
@Schema(description = "推广语")
private String content;
@Schema(description = "资源路径", example = "https://www.iocoder.cn")
private String pagesUrl;
}

View File

@ -0,0 +1,95 @@
package cn.yskj.linghe.module.acdr.controller.admin.notifications;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.CommonResult;
import cn.yskj.linghe.framework.common.util.object.BeanUtils;
import static cn.yskj.linghe.framework.common.pojo.CommonResult.success;
import cn.yskj.linghe.framework.excel.core.util.ExcelUtils;
import cn.yskj.linghe.framework.apilog.core.annotation.ApiAccessLog;
import static cn.yskj.linghe.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.yskj.linghe.module.acdr.controller.admin.notifications.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.notifications.NotificationsDO;
import cn.yskj.linghe.module.acdr.service.notifications.NotificationsService;
@Tag(name = "管理后台 - 通知")
@RestController
@RequestMapping("/acdr/notifications")
@Validated
public class NotificationsController {
@Resource
private NotificationsService notificationsService;
@PostMapping("/create")
@Operation(summary = "创建通知")
@PreAuthorize("@ss.hasPermission('acdr:notifications:create')")
public CommonResult<Long> createNotifications(@Valid @RequestBody NotificationsSaveReqVO createReqVO) {
return success(notificationsService.createNotifications(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新通知")
@PreAuthorize("@ss.hasPermission('acdr:notifications:update')")
public CommonResult<Boolean> updateNotifications(@Valid @RequestBody NotificationsSaveReqVO updateReqVO) {
notificationsService.updateNotifications(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除通知")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('acdr:notifications:delete')")
public CommonResult<Boolean> deleteNotifications(@RequestParam("id") Long id) {
notificationsService.deleteNotifications(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得通知")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('acdr:notifications:query')")
public CommonResult<NotificationsRespVO> getNotifications(@RequestParam("id") Long id) {
NotificationsDO notifications = notificationsService.getNotifications(id);
return success(BeanUtils.toBean(notifications, NotificationsRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得通知分页")
@PreAuthorize("@ss.hasPermission('acdr:notifications:query')")
public CommonResult<PageResult<NotificationsRespVO>> getNotificationsPage(@Valid NotificationsPageReqVO pageReqVO) {
PageResult<NotificationsDO> pageResult = notificationsService.getNotificationsPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, NotificationsRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出通知 Excel")
@PreAuthorize("@ss.hasPermission('acdr:notifications:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportNotificationsExcel(@Valid NotificationsPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<NotificationsDO> list = notificationsService.getNotificationsPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "通知.xls", "数据", NotificationsRespVO.class,
BeanUtils.toBean(list, NotificationsRespVO.class));
}
}

View File

@ -0,0 +1,43 @@
package cn.yskj.linghe.module.acdr.controller.admin.notifications.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.yskj.linghe.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 通知分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotificationsPageReqVO extends PageParam {
@Schema(description = "内容")
private String content;
@Schema(description = "需要跳转的页面", example = "https://www.iocoder.cn")
private String url;
@Schema(description = "消息创建状态", example = "1")
private Integer status;
@Schema(description = "消息类型", example = "1")
private String type;
@Schema(description = "消息创建者", example = "1848")
private Long userId;
@Schema(description = "消息接受范围")
private String receiverScope;
@Schema(description = "标题")
private String title;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,51 @@
package cn.yskj.linghe.module.acdr.controller.admin.notifications.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 通知 Response VO")
@Data
@ExcelIgnoreUnannotated
public class NotificationsRespVO {
@Schema(description = "消息主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "15467")
@ExcelProperty("消息主键")
private Long id;
@Schema(description = "内容")
@ExcelProperty("内容")
private String content;
@Schema(description = "需要跳转的页面", example = "https://www.iocoder.cn")
@ExcelProperty("需要跳转的页面")
private String url;
@Schema(description = "消息创建状态", example = "1")
@ExcelProperty("消息创建状态")
private Integer status;
@Schema(description = "消息类型", example = "1")
@ExcelProperty("消息类型")
private String type;
@Schema(description = "消息创建者", example = "1848")
@ExcelProperty("消息创建者")
private Long userId;
@Schema(description = "消息接受范围")
@ExcelProperty("消息接受范围")
private String receiverScope;
@Schema(description = "标题")
@ExcelProperty("标题")
private String title;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,36 @@
package cn.yskj.linghe.module.acdr.controller.admin.notifications.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 通知新增/修改 Request VO")
@Data
public class NotificationsSaveReqVO {
@Schema(description = "消息主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "15467")
private Long id;
@Schema(description = "内容")
private String content;
@Schema(description = "需要跳转的页面", example = "https://www.iocoder.cn")
private String url;
@Schema(description = "消息创建状态", example = "1")
private Integer status;
@Schema(description = "消息类型", example = "1")
private String type;
@Schema(description = "消息创建者", example = "1848")
private Long userId;
@Schema(description = "消息接受范围")
private String receiverScope;
@Schema(description = "标题")
private String title;
}

View File

@ -0,0 +1,49 @@
package cn.yskj.linghe.module.acdr.dal.dataobject.ad;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.yskj.linghe.framework.mybatis.core.dataobject.BaseDO;
/**
* 推广 DO
*
* @author 芋道源码
*/
@TableName("acdr_ad")
@KeySequence("acdr_ad_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AdDO extends BaseDO {
/**
* 推广ID
*/
@TableId
private Long id;
/**
* 资源类型
*
* 枚举 {@link TODO promotion_type 对应的类}
*/
private Integer type;
/**
* 图片路径
*/
private String imageUrl;
/**
* 推广语
*/
private String content;
/**
* 资源路径
*/
private String pagesUrl;
}

View File

@ -0,0 +1,59 @@
package cn.yskj.linghe.module.acdr.dal.dataobject.notifications;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.yskj.linghe.framework.mybatis.core.dataobject.BaseDO;
/**
* 通知 DO
*
* @author 林河
*/
@TableName("acdr_notifications")
@KeySequence("acdr_notifications_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotificationsDO extends BaseDO {
/**
* 消息主键
*/
@TableId
private Long id;
/**
* 内容
*/
private String content;
/**
* 需要跳转的页面
*/
private String url;
/**
* 消息创建状态
*/
private Integer status;
/**
* 消息类型
*/
private String type;
/**
* 消息创建者
*/
private Long userId;
/**
* 消息接受范围
*/
private String receiverScope;
/**
* 标题
*/
private String title;
}

View File

@ -0,0 +1,30 @@
package cn.yskj.linghe.module.acdr.dal.mysql.ad;
import java.util.*;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.yskj.linghe.framework.mybatis.core.mapper.BaseMapperX;
import cn.yskj.linghe.module.acdr.dal.dataobject.ad.AdDO;
import org.apache.ibatis.annotations.Mapper;
import cn.yskj.linghe.module.acdr.controller.admin.ad.vo.*;
/**
* 推广 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface AdMapper extends BaseMapperX<AdDO> {
default PageResult<AdDO> selectPage(AdPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AdDO>()
.eqIfPresent(AdDO::getType, reqVO.getType())
.eqIfPresent(AdDO::getImageUrl, reqVO.getImageUrl())
.eqIfPresent(AdDO::getContent, reqVO.getContent())
.betweenIfPresent(AdDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(AdDO::getPagesUrl, reqVO.getPagesUrl())
.orderByDesc(AdDO::getId));
}
}

View File

@ -0,0 +1,33 @@
package cn.yskj.linghe.module.acdr.dal.mysql.notifications;
import java.util.*;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.yskj.linghe.framework.mybatis.core.mapper.BaseMapperX;
import cn.yskj.linghe.module.acdr.dal.dataobject.notifications.NotificationsDO;
import org.apache.ibatis.annotations.Mapper;
import cn.yskj.linghe.module.acdr.controller.admin.notifications.vo.*;
/**
* 通知 Mapper
*
* @author 林河
*/
@Mapper
public interface NotificationsMapper extends BaseMapperX<NotificationsDO> {
default PageResult<NotificationsDO> selectPage(NotificationsPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotificationsDO>()
.eqIfPresent(NotificationsDO::getContent, reqVO.getContent())
.eqIfPresent(NotificationsDO::getUrl, reqVO.getUrl())
.eqIfPresent(NotificationsDO::getStatus, reqVO.getStatus())
.eqIfPresent(NotificationsDO::getType, reqVO.getType())
.eqIfPresent(NotificationsDO::getUserId, reqVO.getUserId())
.eqIfPresent(NotificationsDO::getReceiverScope, reqVO.getReceiverScope())
.eqIfPresent(NotificationsDO::getTitle, reqVO.getTitle())
.betweenIfPresent(NotificationsDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotificationsDO::getId));
}
}

View File

@ -0,0 +1,55 @@
package cn.yskj.linghe.module.acdr.service.ad;
import java.util.*;
import jakarta.validation.*;
import cn.yskj.linghe.module.acdr.controller.admin.ad.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.ad.AdDO;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.PageParam;
/**
* 推广 Service 接口
*
* @author 芋道源码
*/
public interface AdService {
/**
* 创建推广
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createAd(@Valid AdSaveReqVO createReqVO);
/**
* 更新推广
*
* @param updateReqVO 更新信息
*/
void updateAd(@Valid AdSaveReqVO updateReqVO);
/**
* 删除推广
*
* @param id 编号
*/
void deleteAd(Long id);
/**
* 获得推广
*
* @param id 编号
* @return 推广
*/
AdDO getAd(Long id);
/**
* 获得推广分页
*
* @param pageReqVO 分页查询
* @return 推广分页
*/
PageResult<AdDO> getAdPage(AdPageReqVO pageReqVO);
}

View File

@ -0,0 +1,74 @@
package cn.yskj.linghe.module.acdr.service.ad;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.yskj.linghe.module.acdr.controller.admin.ad.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.ad.AdDO;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import cn.yskj.linghe.framework.common.util.object.BeanUtils;
import cn.yskj.linghe.module.acdr.dal.mysql.ad.AdMapper;
import static cn.yskj.linghe.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.yskj.linghe.module.acdr.enums.ErrorCodeConstants.*;
/**
* 推广 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class AdServiceImpl implements AdService {
@Resource
private AdMapper adMapper;
@Override
public Long createAd(AdSaveReqVO createReqVO) {
// 插入
AdDO ad = BeanUtils.toBean(createReqVO, AdDO.class);
adMapper.insert(ad);
// 返回
return ad.getId();
}
@Override
public void updateAd(AdSaveReqVO updateReqVO) {
// 校验存在
validateAdExists(updateReqVO.getId());
// 更新
AdDO updateObj = BeanUtils.toBean(updateReqVO, AdDO.class);
adMapper.updateById(updateObj);
}
@Override
public void deleteAd(Long id) {
// 校验存在
validateAdExists(id);
// 删除
adMapper.deleteById(id);
}
private void validateAdExists(Long id) {
if (adMapper.selectById(id) == null) {
throw exception(AD_NOT_EXISTS);
}
}
@Override
public AdDO getAd(Long id) {
return adMapper.selectById(id);
}
@Override
public PageResult<AdDO> getAdPage(AdPageReqVO pageReqVO) {
return adMapper.selectPage(pageReqVO);
}
}

View File

@ -0,0 +1,55 @@
package cn.yskj.linghe.module.acdr.service.notifications;
import java.util.*;
import jakarta.validation.*;
import cn.yskj.linghe.module.acdr.controller.admin.notifications.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.notifications.NotificationsDO;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.PageParam;
/**
* 通知 Service 接口
*
* @author 林河
*/
public interface NotificationsService {
/**
* 创建通知
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createNotifications(@Valid NotificationsSaveReqVO createReqVO);
/**
* 更新通知
*
* @param updateReqVO 更新信息
*/
void updateNotifications(@Valid NotificationsSaveReqVO updateReqVO);
/**
* 删除通知
*
* @param id 编号
*/
void deleteNotifications(Long id);
/**
* 获得通知
*
* @param id 编号
* @return 通知
*/
NotificationsDO getNotifications(Long id);
/**
* 获得通知分页
*
* @param pageReqVO 分页查询
* @return 通知分页
*/
PageResult<NotificationsDO> getNotificationsPage(NotificationsPageReqVO pageReqVO);
}

View File

@ -0,0 +1,74 @@
package cn.yskj.linghe.module.acdr.service.notifications;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.yskj.linghe.module.acdr.controller.admin.notifications.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.notifications.NotificationsDO;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import cn.yskj.linghe.framework.common.pojo.PageParam;
import cn.yskj.linghe.framework.common.util.object.BeanUtils;
import cn.yskj.linghe.module.acdr.dal.mysql.notifications.NotificationsMapper;
import static cn.yskj.linghe.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.yskj.linghe.module.acdr.enums.ErrorCodeConstants.*;
/**
* 通知 Service 实现类
*
* @author 林河
*/
@Service
@Validated
public class NotificationsServiceImpl implements NotificationsService {
@Resource
private NotificationsMapper notificationsMapper;
@Override
public Long createNotifications(NotificationsSaveReqVO createReqVO) {
// 插入
NotificationsDO notifications = BeanUtils.toBean(createReqVO, NotificationsDO.class);
notificationsMapper.insert(notifications);
// 返回
return notifications.getId();
}
@Override
public void updateNotifications(NotificationsSaveReqVO updateReqVO) {
// 校验存在
validateNotificationsExists(updateReqVO.getId());
// 更新
NotificationsDO updateObj = BeanUtils.toBean(updateReqVO, NotificationsDO.class);
notificationsMapper.updateById(updateObj);
}
@Override
public void deleteNotifications(Long id) {
// 校验存在
validateNotificationsExists(id);
// 删除
notificationsMapper.deleteById(id);
}
private void validateNotificationsExists(Long id) {
if (notificationsMapper.selectById(id) == null) {
throw exception(NOTIFICATIONS_NOT_EXISTS);
}
}
@Override
public NotificationsDO getNotifications(Long id) {
return notificationsMapper.selectById(id);
}
@Override
public PageResult<NotificationsDO> getNotificationsPage(NotificationsPageReqVO pageReqVO) {
return notificationsMapper.selectPage(pageReqVO);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.yskj.linghe.module.acdr.dal.mysql.ad.AdMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.yskj.linghe.module.acdr.dal.mysql.notifications.NotificationsMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,146 @@
package cn.yskj.linghe.module.acdr.service.ad;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import jakarta.annotation.Resource;
import cn.yskj.linghe.framework.test.core.ut.BaseDbUnitTest;
import cn.yskj.linghe.module.acdr.controller.admin.ad.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.ad.AdDO;
import cn.yskj.linghe.module.acdr.dal.mysql.ad.AdMapper;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.yskj.linghe.module.acdr.enums.ErrorCodeConstants.*;
import static cn.yskj.linghe.framework.test.core.util.AssertUtils.*;
import static cn.yskj.linghe.framework.test.core.util.RandomUtils.*;
import static cn.yskj.linghe.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.yskj.linghe.framework.common.util.object.ObjectUtils.*;
import static cn.yskj.linghe.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link AdServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(AdServiceImpl.class)
public class AdServiceImplTest extends BaseDbUnitTest {
@Resource
private AdServiceImpl adService;
@Resource
private AdMapper adMapper;
@Test
public void testCreateAd_success() {
// 准备参数
AdSaveReqVO createReqVO = randomPojo(AdSaveReqVO.class).setId(null);
// 调用
Long adId = adService.createAd(createReqVO);
// 断言
assertNotNull(adId);
// 校验记录的属性是否正确
AdDO ad = adMapper.selectById(adId);
assertPojoEquals(createReqVO, ad, "id");
}
@Test
public void testUpdateAd_success() {
// mock 数据
AdDO dbAd = randomPojo(AdDO.class);
adMapper.insert(dbAd);// @Sql: 先插入出一条存在的数据
// 准备参数
AdSaveReqVO updateReqVO = randomPojo(AdSaveReqVO.class, o -> {
o.setId(dbAd.getId()); // 设置更新的 ID
});
// 调用
adService.updateAd(updateReqVO);
// 校验是否更新正确
AdDO ad = adMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, ad);
}
@Test
public void testUpdateAd_notExists() {
// 准备参数
AdSaveReqVO updateReqVO = randomPojo(AdSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> adService.updateAd(updateReqVO), AD_NOT_EXISTS);
}
@Test
public void testDeleteAd_success() {
// mock 数据
AdDO dbAd = randomPojo(AdDO.class);
adMapper.insert(dbAd);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbAd.getId();
// 调用
adService.deleteAd(id);
// 校验数据不存在了
assertNull(adMapper.selectById(id));
}
@Test
public void testDeleteAd_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> adService.deleteAd(id), AD_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值然后删除 @Disabled 注解
public void testGetAdPage() {
// mock 数据
AdDO dbAd = randomPojo(AdDO.class, o -> { // 等会查询到
o.setType(null);
o.setImageUrl(null);
o.setContent(null);
o.setCreateTime(null);
o.setPagesUrl(null);
});
adMapper.insert(dbAd);
// 测试 type 不匹配
adMapper.insert(cloneIgnoreId(dbAd, o -> o.setType(null)));
// 测试 imageUrl 不匹配
adMapper.insert(cloneIgnoreId(dbAd, o -> o.setImageUrl(null)));
// 测试 content 不匹配
adMapper.insert(cloneIgnoreId(dbAd, o -> o.setContent(null)));
// 测试 createTime 不匹配
adMapper.insert(cloneIgnoreId(dbAd, o -> o.setCreateTime(null)));
// 测试 pagesUrl 不匹配
adMapper.insert(cloneIgnoreId(dbAd, o -> o.setPagesUrl(null)));
// 准备参数
AdPageReqVO reqVO = new AdPageReqVO();
reqVO.setType(null);
reqVO.setImageUrl(null);
reqVO.setContent(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setPagesUrl(null);
// 调用
PageResult<AdDO> pageResult = adService.getAdPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbAd, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,158 @@
package cn.yskj.linghe.module.acdr.service.notifications;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import jakarta.annotation.Resource;
import cn.yskj.linghe.framework.test.core.ut.BaseDbUnitTest;
import cn.yskj.linghe.module.acdr.controller.admin.notifications.vo.*;
import cn.yskj.linghe.module.acdr.dal.dataobject.notifications.NotificationsDO;
import cn.yskj.linghe.module.acdr.dal.mysql.notifications.NotificationsMapper;
import cn.yskj.linghe.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.yskj.linghe.module.acdr.enums.ErrorCodeConstants.*;
import static cn.yskj.linghe.framework.test.core.util.AssertUtils.*;
import static cn.yskj.linghe.framework.test.core.util.RandomUtils.*;
import static cn.yskj.linghe.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.yskj.linghe.framework.common.util.object.ObjectUtils.*;
import static cn.yskj.linghe.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link NotificationsServiceImpl} 的单元测试类
*
* @author 林河
*/
@Import(NotificationsServiceImpl.class)
public class NotificationsServiceImplTest extends BaseDbUnitTest {
@Resource
private NotificationsServiceImpl notificationsService;
@Resource
private NotificationsMapper notificationsMapper;
@Test
public void testCreateNotifications_success() {
// 准备参数
NotificationsSaveReqVO createReqVO = randomPojo(NotificationsSaveReqVO.class).setId(null);
// 调用
Long notificationsId = notificationsService.createNotifications(createReqVO);
// 断言
assertNotNull(notificationsId);
// 校验记录的属性是否正确
NotificationsDO notifications = notificationsMapper.selectById(notificationsId);
assertPojoEquals(createReqVO, notifications, "id");
}
@Test
public void testUpdateNotifications_success() {
// mock 数据
NotificationsDO dbNotifications = randomPojo(NotificationsDO.class);
notificationsMapper.insert(dbNotifications);// @Sql: 先插入出一条存在的数据
// 准备参数
NotificationsSaveReqVO updateReqVO = randomPojo(NotificationsSaveReqVO.class, o -> {
o.setId(dbNotifications.getId()); // 设置更新的 ID
});
// 调用
notificationsService.updateNotifications(updateReqVO);
// 校验是否更新正确
NotificationsDO notifications = notificationsMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, notifications);
}
@Test
public void testUpdateNotifications_notExists() {
// 准备参数
NotificationsSaveReqVO updateReqVO = randomPojo(NotificationsSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> notificationsService.updateNotifications(updateReqVO), NOTIFICATIONS_NOT_EXISTS);
}
@Test
public void testDeleteNotifications_success() {
// mock 数据
NotificationsDO dbNotifications = randomPojo(NotificationsDO.class);
notificationsMapper.insert(dbNotifications);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbNotifications.getId();
// 调用
notificationsService.deleteNotifications(id);
// 校验数据不存在了
assertNull(notificationsMapper.selectById(id));
}
@Test
public void testDeleteNotifications_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> notificationsService.deleteNotifications(id), NOTIFICATIONS_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值然后删除 @Disabled 注解
public void testGetNotificationsPage() {
// mock 数据
NotificationsDO dbNotifications = randomPojo(NotificationsDO.class, o -> { // 等会查询到
o.setContent(null);
o.setUrl(null);
o.setStatus(null);
o.setType(null);
o.setUserId(null);
o.setReceiverScope(null);
o.setTitle(null);
o.setCreateTime(null);
});
notificationsMapper.insert(dbNotifications);
// 测试 content 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setContent(null)));
// 测试 url 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setUrl(null)));
// 测试 status 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setStatus(null)));
// 测试 type 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setType(null)));
// 测试 userId 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setUserId(null)));
// 测试 receiverScope 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setReceiverScope(null)));
// 测试 title 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setTitle(null)));
// 测试 createTime 不匹配
notificationsMapper.insert(cloneIgnoreId(dbNotifications, o -> o.setCreateTime(null)));
// 准备参数
NotificationsPageReqVO reqVO = new NotificationsPageReqVO();
reqVO.setContent(null);
reqVO.setUrl(null);
reqVO.setStatus(null);
reqVO.setType(null);
reqVO.setUserId(null);
reqVO.setReceiverScope(null);
reqVO.setTitle(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<NotificationsDO> pageResult = notificationsService.getNotificationsPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifications, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,53 @@
import request from '@/utils/request'
// 创建通知
export function createNotifications(data) {
return request({
url: '/acdr/notifications/create',
method: 'post',
data: data
})
}
// 更新通知
export function updateNotifications(data) {
return request({
url: '/acdr/notifications/update',
method: 'put',
data: data
})
}
// 删除通知
export function deleteNotifications(id) {
return request({
url: '/acdr/notifications/delete?id=' + id,
method: 'delete'
})
}
// 获得通知
export function getNotifications(id) {
return request({
url: '/acdr/notifications/get?id=' + id,
method: 'get'
})
}
// 获得通知分页
export function getNotificationsPage(params) {
return request({
url: '/acdr/notifications/page',
method: 'get',
params
})
}
// 导出通知 Excel
export function exportNotificationsExcel(params) {
return request({
url: '/acdr/notifications/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}

View File

@ -0,0 +1,130 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="内容">
<Editor v-model="formData.content" :min-height="192"/>
</el-form-item>
<el-form-item label="需要跳转的页面" prop="url">
<el-input v-model="formData.url" placeholder="请输入需要跳转的页面" />
</el-form-item>
<el-form-item label="消息创建状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="消息类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择消息类型">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息创建者" prop="userId">
<el-input v-model="formData.userId" placeholder="请输入消息创建者" />
</el-form-item>
<el-form-item label="消息接受范围" prop="receiverScope">
<el-input v-model="formData.receiverScope" placeholder="请输入消息接受范围" />
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as NotificationsApi from '@/api/acdr/notifications';
import Editor from '@/components/Editor';
export default {
name: "NotificationsForm",
components: {
Editor,
},
data() {
return {
//
dialogTitle: "",
//
dialogVisible: false,
// 12
formLoading: false,
//
formData: {
id: undefined,
content: undefined,
url: undefined,
status: undefined,
type: undefined,
userId: undefined,
receiverScope: undefined,
title: undefined,
},
//
formRules: {
},
};
},
methods: {
/** 打开弹窗 */
async open(id) {
this.dialogVisible = true;
this.reset();
//
if (id) {
this.formLoading = true;
try {
const res = await NotificationsApi.getNotifications(id);
this.formData = res.data;
this.title = "修改通知";
} finally {
this.formLoading = false;
}
}
this.title = "新增通知";
},
/** 提交按钮 */
async submitForm() {
//
await this.$refs["formRef"].validate();
this.formLoading = true;
try {
const data = this.formData;
//
if (data.id) {
await NotificationsApi.updateNotifications(data);
this.$modal.msgSuccess("修改成功");
this.dialogVisible = false;
this.$emit('success');
return;
}
//
await NotificationsApi.createNotifications(data);
this.$modal.msgSuccess("新增成功");
this.dialogVisible = false;
this.$emit('success');
} finally {
this.formLoading = false;
}
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
content: undefined,
url: undefined,
status: undefined,
type: undefined,
userId: undefined,
receiverScope: undefined,
title: undefined,
};
this.resetForm("formRef");
}
}
};
</script>

View File

@ -0,0 +1,175 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="需要跳转的页面" prop="url">
<el-input v-model="queryParams.url" placeholder="请输入需要跳转的页面" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="消息创建状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择消息创建状态" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择消息类型" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息创建者" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入消息创建者" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="消息接受范围" prop="receiverScope">
<el-input v-model="queryParams.receiverScope" placeholder="请输入消息接受范围" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['acdr:notifications:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['acdr:notifications:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="消息主键" align="center" prop="id" />
<el-table-column label="内容" align="center" prop="content" />
<el-table-column label="需要跳转的页面" align="center" prop="url" />
<el-table-column label="消息创建状态" align="center" prop="status" />
<el-table-column label="消息类型" align="center" prop="type" />
<el-table-column label="消息创建者" align="center" prop="userId" />
<el-table-column label="消息接受范围" align="center" prop="receiverScope" />
<el-table-column label="标题" align="center" prop="title" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['acdr:notifications:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['acdr:notifications:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<NotificationsForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as NotificationsApi from '@/api/acdr/notifications';
import NotificationsForm from './NotificationsForm.vue';
export default {
name: "Notifications",
components: {
NotificationsForm,
},
data() {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
isExpandAll: true,
//
refreshTable: true,
//
currentRow: {},
//
queryParams: {
pageNo: 1,
pageSize: 10,
content: null,
url: null,
status: null,
type: null,
userId: null,
receiverScope: null,
title: null,
createTime: [],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
async getList() {
try {
this.loading = true;
const res = await NotificationsApi.getNotificationsPage(this.queryParams);
this.list = res.data.list;
this.total = res.data.total;
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.$refs["formRef"].open(id);
},
/** 删除按钮操作 */
async handleDelete(row) {
const id = row.id;
await this.$modal.confirm('是否确认删除通知编号为"' + id + '"的数据项?')
try {
await NotificationsApi.deleteNotifications(id);
await this.getList();
this.$modal.msgSuccess("删除成功");
} catch {}
},
/** 导出按钮操作 */
async handleExport() {
await this.$modal.confirm('是否确认导出所有通知数据项?');
try {
this.exportLoading = true;
const res = await NotificationsApi.exportNotificationsExcel(this.queryParams);
this.$download.excel(res.data, '通知.xls');
} catch {
} finally {
this.exportLoading = false;
}
},
}
};
</script>

View File

@ -0,0 +1,43 @@
import request from '@/config/axios'
// 推广 VO
export interface AdVO {
id: number // 推广ID
type: number // 资源类型
imageUrl: string // 图片路径
content: string // 推广语
pagesUrl: string // 资源路径
}
// 推广 API
export const AdApi = {
// 查询推广分页
getAdPage: async (params: any) => {
return await request.get({ url: `/acdr/ad/page`, params })
},
// 查询推广详情
getAd: async (id: number) => {
return await request.get({ url: `/acdr/ad/get?id=` + id })
},
// 新增推广
createAd: async (data: AdVO) => {
return await request.post({ url: `/acdr/ad/create`, data })
},
// 修改推广
updateAd: async (data: AdVO) => {
return await request.put({ url: `/acdr/ad/update`, data })
},
// 删除推广
deleteAd: async (id: number) => {
return await request.delete({ url: `/acdr/ad/delete?id=` + id })
},
// 导出推广 Excel
exportAd: async (params) => {
return await request.download({ url: `/acdr/ad/export-excel`, params })
},
}

View File

@ -0,0 +1,46 @@
import request from '@/config/axios'
// 通知 VO
export interface NotificationsVO {
id: number // 消息主键
content: string // 内容
url: string // 需要跳转的页面
status: number // 消息创建状态
type: string // 消息类型
userId: number // 消息创建者
receiverScope: string // 消息接受范围
title: string // 标题
}
// 通知 API
export const NotificationsApi = {
// 查询通知分页
getNotificationsPage: async (params: any) => {
return await request.get({ url: `/acdr/notifications/page`, params })
},
// 查询通知详情
getNotifications: async (id: number) => {
return await request.get({ url: `/acdr/notifications/get?id=` + id })
},
// 新增通知
createNotifications: async (data: NotificationsVO) => {
return await request.post({ url: `/acdr/notifications/create`, data })
},
// 修改通知
updateNotifications: async (data: NotificationsVO) => {
return await request.put({ url: `/acdr/notifications/update`, data })
},
// 删除通知
deleteNotifications: async (id: number) => {
return await request.delete({ url: `/acdr/notifications/delete?id=` + id })
},
// 导出通知 Excel
exportNotifications: async (params) => {
return await request.download({ url: `/acdr/notifications/export-excel`, params })
},
}

View File

@ -0,0 +1,114 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="资源类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择资源类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="图片路径" prop="imageUrl">
<UploadImg v-model="formData.imageUrl" />
</el-form-item>
<el-form-item label="推广语" prop="content">
<Editor v-model="formData.content" height="150px" />
</el-form-item>
<el-form-item label="资源路径" prop="pagesUrl">
<el-input v-model="formData.pagesUrl" placeholder="请输入资源路径" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { AdApi, AdVO } from '@/api/acdr/ad'
/** 推广 表单 */
defineOptions({ name: 'AdForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
type: undefined,
imageUrl: undefined,
content: undefined,
pagesUrl: undefined,
})
const formRules = reactive({
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await AdApi.getAd(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as AdVO
if (formType.value === 'create') {
await AdApi.createAd(data)
message.success(t('common.createSuccess'))
} else {
await AdApi.updateAd(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
type: undefined,
imageUrl: undefined,
content: undefined,
pagesUrl: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,213 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="资源类型" prop="type">
<el-select
v-model="queryParams.type"
placeholder="请选择资源类型"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="资源路径" prop="pagesUrl">
<el-input
v-model="queryParams.pagesUrl"
placeholder="请输入资源路径"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['acdr:ad:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['acdr:ad:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="推广ID" align="center" prop="id" />
<el-table-column label="资源类型" align="center" prop="type">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="图片路径" align="center" prop="imageUrl" />
<el-table-column label="推广语" align="center" prop="content" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="资源路径" align="center" prop="pagesUrl" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['acdr:ad:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['acdr:ad:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<AdForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { AdApi, AdVO } from '@/api/acdr/ad'
import AdForm from './AdForm.vue'
/** 推广 列表 */
defineOptions({ name: 'Ad' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<AdVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: undefined,
imageUrl: undefined,
content: undefined,
createTime: [],
pagesUrl: undefined,
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await AdApi.getAdPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await AdApi.deleteAd(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await AdApi.exportAd(queryParams)
download.excel(data, '推广.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -0,0 +1,125 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="内容" prop="content">
<Editor v-model="formData.content" height="150px" />
</el-form-item>
<el-form-item label="需要跳转的页面" prop="url">
<el-input v-model="formData.url" placeholder="请输入需要跳转的页面" />
</el-form-item>
<el-form-item label="消息创建状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="消息类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择消息类型">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息创建者" prop="userId">
<el-input v-model="formData.userId" placeholder="请输入消息创建者" />
</el-form-item>
<el-form-item label="消息接受范围" prop="receiverScope">
<el-input v-model="formData.receiverScope" placeholder="请输入消息接受范围" />
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { NotificationsApi, NotificationsVO } from '@/api/acdr/notifications'
/** 通知 表单 */
defineOptions({ name: 'NotificationsForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
content: undefined,
url: undefined,
status: undefined,
type: undefined,
userId: undefined,
receiverScope: undefined,
title: undefined,
})
const formRules = reactive({
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await NotificationsApi.getNotifications(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as NotificationsVO
if (formType.value === 'create') {
await NotificationsApi.createNotifications(data)
message.success(t('common.createSuccess'))
} else {
await NotificationsApi.updateNotifications(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
content: undefined,
url: undefined,
status: undefined,
type: undefined,
userId: undefined,
receiverScope: undefined,
title: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,246 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="需要跳转的页面" prop="url">
<el-input
v-model="queryParams.url"
placeholder="请输入需要跳转的页面"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="消息创建状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择消息创建状态"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息类型" prop="type">
<el-select
v-model="queryParams.type"
placeholder="请选择消息类型"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="消息创建者" prop="userId">
<el-input
v-model="queryParams.userId"
placeholder="请输入消息创建者"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="消息接受范围" prop="receiverScope">
<el-input
v-model="queryParams.receiverScope"
placeholder="请输入消息接受范围"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入标题"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['acdr:notifications:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['acdr:notifications:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="消息主键" align="center" prop="id" />
<el-table-column label="内容" align="center" prop="content" />
<el-table-column label="需要跳转的页面" align="center" prop="url" />
<el-table-column label="消息创建状态" align="center" prop="status" />
<el-table-column label="消息类型" align="center" prop="type" />
<el-table-column label="消息创建者" align="center" prop="userId" />
<el-table-column label="消息接受范围" align="center" prop="receiverScope" />
<el-table-column label="标题" align="center" prop="title" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['acdr:notifications:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['acdr:notifications:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<NotificationsForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { NotificationsApi, NotificationsVO } from '@/api/acdr/notifications'
import NotificationsForm from './NotificationsForm.vue'
/** 通知 列表 */
defineOptions({ name: 'Notifications' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<NotificationsVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
content: undefined,
url: undefined,
status: undefined,
type: undefined,
userId: undefined,
receiverScope: undefined,
title: undefined,
createTime: [],
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await NotificationsApi.getNotificationsPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await NotificationsApi.deleteNotifications(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await NotificationsApi.exportNotifications(queryParams)
download.excel(data, '通知.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -62,5 +62,8 @@
"vite.config.ts": "tsconfig.*.json,uno.config.ts,tsconfig.json,uni-pages.d.ts",
"manifest.config.ts": "manifest.config.ts,pages.config.ts"
},
"vue3snippets.enable-compile-vue-file-on-did-save-code": true
"vue3snippets.enable-compile-vue-file-on-did-save-code": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}

View File

@ -16,7 +16,11 @@
:src="'/static/certification/certification_bg.png'"
mode="aspectFill"
></image>
<view class="flex justify-center items-center h-screen bg-gray-100 pt-10">
<!-- 如果没有申请就显示这里申请显示申请状态 -->
<view
v-if="!applyState || user.userInfo.isPetNursery"
class="flex justify-center items-center h-screen bg-gray-100 pt-10"
>
<view class="text-center flex gap-5 flex-col" v-if="!user.userInfo.isPetNursery">
<text class="text-xl text-gray-700 mb-4">您还未认证加入我们成为宠托师</text>
<image
@ -61,6 +65,13 @@
</view>
</view>
</view>
<view v-else>
<view
class="z-999 absolute top-[48%] left-[50%] transform-translate-x-[-50%] font-800 text-size-2xl"
>
{{ applyStateData }}
</view>
</view>
<wd-popup
:close-on-click-modal="false"
v-model="show"
@ -81,7 +92,9 @@ import { imgUrl } from '@/utils/commUtils'
const user = useUserStore()
const certificate = ref({}) //
const show = ref(!user.userInfo.isPetNursery)
const show = ref(!user.userInfo.isPetNursery && false)
const applyState = ref(false)
const applyStateData = ref('')
const handleReturn = () => {
show.value = false
@ -94,7 +107,21 @@ const handleJoin = () => {
//
const getCertificationStatus = async () => {
certificate.value = await httpGet('/petInfo/getExpertInfo')
const res = await httpGet('/petInfo/getExpertInfo')
if (res.code == 200) {
certificate.value = res.data
}
//
else {
const stateRes = await httpGet('/petInfo/getApplyState')
if (stateRes.code == 200) {
//
applyState.value = true
applyStateData.value = stateRes.data
} else {
//
}
}
}
//

View File

@ -2,33 +2,55 @@ import os
import shutil
import pymysql
import tkinter as tk
from tkinter import filedialog, messagebox, StringVar
from tkinter import filedialog, messagebox, StringVar, BooleanVar
import configparser
# 执行 SQL 文件
def execute_sql_file(sql_file_path, db_config):
connection = None
cursor = None
try:
# 连接 MySQL 数据库
connection = pymysql.connect(
host=db_config['host'],
user=db_config['user'],
password=db_config['password'],
database=db_config['database'],
port=int(db_config['port'])
host=db_config["host"],
user=db_config["user"],
password=db_config["password"],
database=db_config["database"],
port=int(db_config["port"]),
)
cursor = connection.cursor()
with open(sql_file_path, 'r', encoding='utf-8') as file:
# 读取并处理 SQL 文件
with open(sql_file_path, "r", encoding="utf-8") as file:
sql_script = file.read()
cursor.execute(sql_script)
# 拆分 SQL 语句
statements = sql_script.split(";")
for statement in statements:
# 去除空的语句
if statement.strip():
cursor.execute(statement)
print(f"执行 SQL 语句: {statement.strip()}")
# 提交事务
connection.commit()
messagebox.showinfo("SQL执行成功", f"SQL文件 {sql_file_path} 已成功执行")
except Exception as e:
messagebox.showerror("SQL执行失败", f"执行SQL文件时出错: {e}")
if connection:
connection.rollback()
finally:
cursor.close()
connection.close()
if cursor:
cursor.close()
if connection:
connection.close()
# 处理解压后的文件
def handle_extracted_files(temp_extract_dir, output_dir, ui_dir, db_config):
def handle_extracted_files(
temp_extract_dir, output_dir, ui_dir, db_config, execute_sql
):
for root_dir, dirs, files in os.walk(temp_extract_dir):
for file in files:
src_file_path = os.path.join(root_dir, file)
@ -41,7 +63,7 @@ def handle_extracted_files(temp_extract_dir, output_dir, ui_dir, db_config):
shutil.copy2(src_file_path, dest_file_path)
print(f"复制文件 {src_file_path}{dest_file_path}")
elif "sql" in top_level_dir and file == "sql.sql":
elif "sql" in top_level_dir and file == "sql.sql" and execute_sql:
execute_sql_file(src_file_path, db_config)
print(f"执行SQL文件: {src_file_path}")
@ -51,18 +73,37 @@ def handle_extracted_files(temp_extract_dir, output_dir, ui_dir, db_config):
shutil.copy2(src_file_path, dest_file_path)
print(f"复制文件 {src_file_path}{dest_file_path}")
# 选择zip文件
def select_zip_file():
zip_file_path.set(filedialog.askopenfilename(filetypes=[("ZIP files", "*.zip")]))
# 选择输出目录
def select_output_dir():
output_dir.set(filedialog.askdirectory())
# 选择UI目录
def select_ui_dir():
ui_dir.set(filedialog.askdirectory())
# 保存配置信息到 .ini 文件
def save_config_to_ini():
config = configparser.ConfigParser()
config["DATABASE"] = {
"host": host_var.get(),
"user": user_var.get(),
"password": password_var.get(),
"database": db_var.get(),
"port": port_var.get(),
}
with open("config.ini", "w") as configfile:
config.write(configfile)
messagebox.showinfo("保存成功", "数据库配置信息已保存到 config.ini 文件中")
# 解压并处理文件
def extract_and_process_files():
if not zip_file_path.get() or not output_dir.get() or not ui_dir.get():
@ -71,23 +112,31 @@ def extract_and_process_files():
# 设置数据库连接参数
db_config = {
'host': host_var.get() or 'localhost',
'user': user_var.get() or 'root',
'password': password_var.get() or 'root',
'database': db_var.get() or 'cwet',
'port': port_var.get() or 3306
"host": host_var.get() or "localhost",
"user": user_var.get() or "root",
"password": password_var.get() or "root",
"database": db_var.get() or "cwet",
"port": port_var.get() or 3306,
}
# 解压ZIP文件
temp_extract_dir = os.path.join(output_dir.get(), "temp_extracted")
shutil.unpack_archive(zip_file_path.get(), temp_extract_dir)
# 检查是否选择了执行 SQL
if execute_sql_var.get():
# 解压ZIP文件并执行 SQL
temp_extract_dir = os.path.join(output_dir.get(), "temp_extracted")
shutil.unpack_archive(zip_file_path.get(), temp_extract_dir)
# 处理解压后的文件
handle_extracted_files(temp_extract_dir, output_dir.get(), ui_dir.get(), db_config)
# 处理解压后的文件
handle_extracted_files(
temp_extract_dir, output_dir.get(), ui_dir.get(), db_config, True
)
# 清理临时文件夹
shutil.rmtree(temp_extract_dir)
messagebox.showinfo("完成", "文件处理完成!")
else:
# 保存配置到 ini 文件
save_config_to_ini()
# 清理临时文件夹
shutil.rmtree(temp_extract_dir)
messagebox.showinfo("完成", "文件处理完成!")
# 创建主窗口
root = tk.Tk()
@ -99,24 +148,35 @@ output_dir = StringVar()
ui_dir = StringVar()
# 数据库连接参数
host_var = StringVar(value='localhost')
user_var = StringVar(value='root')
password_var = StringVar(value='root')
db_var = StringVar(value='cwet')
port_var = StringVar(value='3306')
host_var = StringVar(value="localhost")
user_var = StringVar(value="root")
password_var = StringVar(value="root")
db_var = StringVar(value="cwet")
port_var = StringVar(value="3306")
# 是否执行 SQL 的选择
execute_sql_var = BooleanVar(value=True)
# GUI布局
tk.Label(root, text="选择ZIP文件:").grid(row=0, column=0, padx=10, pady=5)
tk.Entry(root, textvariable=zip_file_path, width=50).grid(row=0, column=1, padx=10, pady=5)
tk.Button(root, text="选择ZIP文件", command=select_zip_file).grid(row=0, column=2, padx=10, pady=5)
tk.Entry(root, textvariable=zip_file_path, width=50).grid(
row=0, column=1, padx=10, pady=5
)
tk.Button(root, text="选择ZIP文件", command=select_zip_file).grid(
row=0, column=2, padx=10, pady=5
)
tk.Label(root, text="选择输出目录:").grid(row=1, column=0, padx=10, pady=5)
tk.Entry(root, textvariable=output_dir, width=50).grid(row=1, column=1, padx=10, pady=5)
tk.Button(root, text="选择输出目录", command=select_output_dir).grid(row=1, column=2, padx=10, pady=5)
tk.Button(root, text="选择输出目录", command=select_output_dir).grid(
row=1, column=2, padx=10, pady=5
)
tk.Label(root, text="选择UI目录:").grid(row=2, column=0, padx=10, pady=5)
tk.Entry(root, textvariable=ui_dir, width=50).grid(row=2, column=1, padx=10, pady=5)
tk.Button(root, text="选择UI目录", command=select_ui_dir).grid(row=2, column=2, padx=10, pady=5)
tk.Button(root, text="选择UI目录", command=select_ui_dir).grid(
row=2, column=2, padx=10, pady=5
)
# 数据库设置
tk.Label(root, text="数据库主机:").grid(row=3, column=0, padx=10, pady=5)
@ -134,7 +194,15 @@ tk.Entry(root, textvariable=db_var).grid(row=6, column=1, padx=10, pady=5)
tk.Label(root, text="端口:").grid(row=7, column=0, padx=10, pady=5)
tk.Entry(root, textvariable=port_var).grid(row=7, column=1, padx=10, pady=5)
tk.Button(root, text="开始处理", command=extract_and_process_files).grid(row=8, column=1, padx=10, pady=20)
# 执行 SQL 的复选框
tk.Checkbutton(root, text="执行SQL语句", variable=execute_sql_var).grid(
row=8, column=0, columnspan=2, padx=10, pady=5
)
# 开始处理按钮
tk.Button(root, text="开始处理", command=extract_and_process_files).grid(
row=9, column=1, padx=10, pady=20
)
# 运行应用
root.mainloop()

View File

@ -16,7 +16,7 @@ public class TableToEntityConstructor {
// 新版本的代码生成器
public static void AutoTable(String moduleName, String entityPackage, String mapperPackage, String servicePackage, String... tables) {
FastAutoGenerator.create(
"jdbc:mysql://localhost:3306/acdr?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false",
"jdbc:mysql://localhost:3306/cwet?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false",
"root",
"root")
.globalConfig(builder -> {
@ -74,7 +74,7 @@ public class TableToEntityConstructor {
}
public static void main(String[] args) {
AutoTable("chat", "", "", "",
"acdr_chat_message");
AutoTable("ad", "", "", "",
"acdr_ad");
}
}

View File

@ -0,0 +1,71 @@
package com.yskj.acdr.master.ad.controller;
import com.yskj.acdr.common.response.GlobalResponse;
import com.yskj.acdr.master.ad.entity.Ad;
import com.yskj.acdr.master.ad.service.IAdService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.validation.groups.Default;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 推广表 前端控制器
* </p>
*
* @author 林河
* @since 2024-09-12
*/
@Api(tags = "推广表")
@RestController
@RequestMapping("/ad")
public class AdController {
@Autowired
private IAdService service;
@ApiOperation(value = "推广表分页列表", response = Ad.class)
@PostMapping(value = "/page")
public GlobalResponse<Ad> list(GlobalResponse<Ad> page) {
return service.lambdaQuery().page(page);
}
@ApiOperation(value = "推广表详情", response = Ad.class)
@GetMapping(value = "/info/{id}")
public GlobalResponse<Ad> info(@Validated({GetMapping.class}) @PathVariable Long id) {
Ad ad = service.getById(id);
return GlobalResponse.success(ad);
}
@ApiOperation(value = "推广表新增")
@PostMapping(value = "/add")
public GlobalResponse<Ad> add(@Validated({PostMapping.class, Default.class}) @RequestBody Ad param) {
service.save(param);
return GlobalResponse.success("推广表新增成功!");
}
@ApiOperation(value = "推广表修改")
@PostMapping(value = "/modify")
public GlobalResponse<Ad> modify(@Validated({PutMapping.class, Default.class}) @RequestBody Ad param) {
service.updateById(param);
return GlobalResponse.success("推广表修改成功!");
}
@ApiOperation(value = "推广表删除(单个条目)")
@GetMapping(value = "/remove/{id}")
public GlobalResponse<Ad> remove(@Validated({DeleteMapping.class}) @PathVariable Long id) {
service.removeById(id);
return GlobalResponse.success("推广表删除(单个条目)");
}
@ApiOperation(value = "推广表删除(多个条目)")
@PostMapping(value = "/removes")
public GlobalResponse<Ad> removes(@RequestBody List<Long> ids) {
service.removeBatchByIds(ids);
return GlobalResponse.success("推广表删除(多个条目)");
}
}

View File

@ -0,0 +1,52 @@
package com.yskj.acdr.master.ad.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.yskj.acdr.master.common.ExtendEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 推广表
* </p>
*
* @author 林河
* @since 2024-09-12
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
@TableName("acdr_ad")
@ApiModel(value = "Ad对象", description = "推广表")
public class Ad extends ExtendEntity implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("推广ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@ApiModelProperty("资源类型")
@TableField("type")
private Integer type;
@ApiModelProperty("图片路径")
@TableField("image_url")
private String imageUrl;
@ApiModelProperty("推广语")
@TableField("content")
private String content;
@ApiModelProperty("资源路径")
@TableField("pages_url")
private String pagesUrl;
}

View File

@ -0,0 +1,18 @@
package com.yskj.acdr.master.ad.mapper;
import com.yskj.acdr.master.ad.entity.Ad;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 推广表 Mapper 接口
* </p>
*
* @author 林河
* @since 2024-09-12
*/
@Mapper
public interface AdMapper extends BaseMapper<Ad> {
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yskj.acdr.master.ad.mapper.AdMapper">
</mapper>

View File

@ -0,0 +1,16 @@
package com.yskj.acdr.master.ad.service;
import com.yskj.acdr.master.ad.entity.Ad;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 推广表 服务类
* </p>
*
* @author 林河
* @since 2024-09-12
*/
public interface IAdService extends IService<Ad> {
}

View File

@ -0,0 +1,20 @@
package com.yskj.acdr.master.ad.service.impl;
import com.yskj.acdr.master.ad.entity.Ad;
import com.yskj.acdr.master.ad.mapper.AdMapper;
import com.yskj.acdr.master.ad.service.IAdService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 推广表 服务实现类
* </p>
*
* @author 林河
* @since 2024-09-12
*/
@Service
public class AdServiceImpl extends ServiceImpl<AdMapper, Ad> implements IAdService {
}

View File

@ -28,7 +28,8 @@ public class ExtendEntity {
// 逻辑删除
@TableLogic
private Boolean deleted;
@TableField(value = "deleted", fill = FieldFill.INSERT)
private Boolean deleted = false;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;

View File

@ -13,6 +13,7 @@ import com.yskj.acdr.master.pet.entity.PetSpecialistCertificate;
import com.yskj.acdr.master.pet.service.PetExpertCertificationService;
import com.yskj.acdr.master.pet.service.PetInfoService;
import com.yskj.acdr.master.pet.service.PetSpecialistCertificateService;
import com.yskj.acdr.master.pet.status.PetExpertCertificationStatus;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.groups.Default;
@ -166,5 +167,18 @@ public class PetController {
if (one == null || one.isEmpty()) return GlobalResponse.success();
return GlobalResponse.success(one.getFirst());
}
/**
* 获取当前宠托师的申请进度/状态
*/
@GetMapping("/getApplyState")
public GlobalResponse<PetExpertCertificationStatus> getApplyState() {
// 获取当前用户的申请状态
PetExpertCertification one = pecs.lambdaQuery()
.eq(PetExpertCertification::getUserId, StpUtil.getLoginIdAsLong())
.one();
if (one == null) return GlobalResponse.failure("你还没有申请成为宠托师!");
return GlobalResponse.success(one.getState());
}
}

View File

@ -51,7 +51,7 @@ public class AuthController {
}
@ApiOperation("判断是否对手机进行实名认证")
@ApiOperation("判断是否对手机进行认证")
@GetMapping("/isPhoneBind")
public GlobalResponse<Boolean> isUsePhone() {
Users user = usersService.getById(StpUtil.getLoginIdAsLong());

View File

@ -27,8 +27,8 @@ import java.time.LocalDateTime;
@ToString
@Accessors(chain = true)
@TableName("acdr_users")
@ApiModel(value = "UserBase对象", description = "")
public class Users implements Serializable {
@ApiModel(value = "Users对象", description = "")
public class Users extends ExtendEntity implements Serializable {
private static final long serialVersionUID = 1L;
@ -81,14 +81,4 @@ public class Users implements Serializable {
@ApiModelProperty("是否拥有宠托师证书")
@TableField(value = "is_pet_nursery")
private Boolean isPetNursery = false;
// 逻辑删除
@TableLogic
private Boolean deleted;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

View File

@ -313,8 +313,9 @@ public class AuthenticationService {
* 判断该当前用户是否经过实名认证
*/
public boolean isRealName() {
long userId = StpUtil.getLoginIdAsLong();
var query = new LambdaQueryWrapper<UserIdentityVerification>()
.eq(UserIdentityVerification::getUserId, StpUtil.getLoginIdAsLong());
.eq(UserIdentityVerification::getUserId, userId);
var userIdEn = uidmapper.selectOne(query);
return userIdEn != null;
}