推广表,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 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", "vite.config.ts": "tsconfig.*.json,uno.config.ts,tsconfig.json,uni-pages.d.ts",
"manifest.config.ts": "manifest.config.ts,pages.config.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'" :src="'/static/certification/certification_bg.png'"
mode="aspectFill" mode="aspectFill"
></image> ></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"> <view class="text-center flex gap-5 flex-col" v-if="!user.userInfo.isPetNursery">
<text class="text-xl text-gray-700 mb-4">您还未认证加入我们成为宠托师</text> <text class="text-xl text-gray-700 mb-4">您还未认证加入我们成为宠托师</text>
<image <image
@ -61,6 +65,13 @@
</view> </view>
</view> </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 <wd-popup
:close-on-click-modal="false" :close-on-click-modal="false"
v-model="show" v-model="show"
@ -81,7 +92,9 @@ import { imgUrl } from '@/utils/commUtils'
const user = useUserStore() const user = useUserStore()
const certificate = ref({}) // 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 = () => { const handleReturn = () => {
show.value = false show.value = false
@ -94,7 +107,21 @@ const handleJoin = () => {
// //
const getCertificationStatus = async () => { 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 shutil
import pymysql import pymysql
import tkinter as tk import tkinter as tk
from tkinter import filedialog, messagebox, StringVar from tkinter import filedialog, messagebox, StringVar, BooleanVar
import configparser
# 执行 SQL 文件 # 执行 SQL 文件
def execute_sql_file(sql_file_path, db_config): def execute_sql_file(sql_file_path, db_config):
connection = None
cursor = None
try: try:
# 连接 MySQL 数据库 # 连接 MySQL 数据库
connection = pymysql.connect( connection = pymysql.connect(
host=db_config['host'], host=db_config["host"],
user=db_config['user'], user=db_config["user"],
password=db_config['password'], password=db_config["password"],
database=db_config['database'], database=db_config["database"],
port=int(db_config['port']) port=int(db_config["port"]),
) )
cursor = connection.cursor() 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() 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() connection.commit()
messagebox.showinfo("SQL执行成功", f"SQL文件 {sql_file_path} 已成功执行") messagebox.showinfo("SQL执行成功", f"SQL文件 {sql_file_path} 已成功执行")
except Exception as e: except Exception as e:
messagebox.showerror("SQL执行失败", f"执行SQL文件时出错: {e}") messagebox.showerror("SQL执行失败", f"执行SQL文件时出错: {e}")
if connection:
connection.rollback()
finally: finally:
cursor.close() if cursor:
connection.close() 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 root_dir, dirs, files in os.walk(temp_extract_dir):
for file in files: for file in files:
src_file_path = os.path.join(root_dir, file) 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) shutil.copy2(src_file_path, dest_file_path)
print(f"复制文件 {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) execute_sql_file(src_file_path, db_config)
print(f"执行SQL文件: {src_file_path}") 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) shutil.copy2(src_file_path, dest_file_path)
print(f"复制文件 {src_file_path}{dest_file_path}") print(f"复制文件 {src_file_path}{dest_file_path}")
# 选择zip文件 # 选择zip文件
def select_zip_file(): def select_zip_file():
zip_file_path.set(filedialog.askopenfilename(filetypes=[("ZIP files", "*.zip")])) zip_file_path.set(filedialog.askopenfilename(filetypes=[("ZIP files", "*.zip")]))
# 选择输出目录 # 选择输出目录
def select_output_dir(): def select_output_dir():
output_dir.set(filedialog.askdirectory()) output_dir.set(filedialog.askdirectory())
# 选择UI目录 # 选择UI目录
def select_ui_dir(): def select_ui_dir():
ui_dir.set(filedialog.askdirectory()) 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(): def extract_and_process_files():
if not zip_file_path.get() or not output_dir.get() or not ui_dir.get(): 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 = { db_config = {
'host': host_var.get() or 'localhost', "host": host_var.get() or "localhost",
'user': user_var.get() or 'root', "user": user_var.get() or "root",
'password': password_var.get() or 'root', "password": password_var.get() or "root",
'database': db_var.get() or 'cwet', "database": db_var.get() or "cwet",
'port': port_var.get() or 3306 "port": port_var.get() or 3306,
} }
# 解压ZIP文件 # 检查是否选择了执行 SQL
temp_extract_dir = os.path.join(output_dir.get(), "temp_extracted") if execute_sql_var.get():
shutil.unpack_archive(zip_file_path.get(), temp_extract_dir) # 解压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() root = tk.Tk()
@ -99,24 +148,35 @@ output_dir = StringVar()
ui_dir = StringVar() ui_dir = StringVar()
# 数据库连接参数 # 数据库连接参数
host_var = StringVar(value='localhost') host_var = StringVar(value="localhost")
user_var = StringVar(value='root') user_var = StringVar(value="root")
password_var = StringVar(value='root') password_var = StringVar(value="root")
db_var = StringVar(value='cwet') db_var = StringVar(value="cwet")
port_var = StringVar(value='3306') port_var = StringVar(value="3306")
# 是否执行 SQL 的选择
execute_sql_var = BooleanVar(value=True)
# GUI布局 # GUI布局
tk.Label(root, text="选择ZIP文件:").grid(row=0, column=0, padx=10, pady=5) 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.Entry(root, textvariable=zip_file_path, width=50).grid(
tk.Button(root, text="选择ZIP文件", command=select_zip_file).grid(row=0, column=2, padx=10, pady=5) 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.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.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.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.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) 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.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.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() 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) { public static void AutoTable(String moduleName, String entityPackage, String mapperPackage, String servicePackage, String... tables) {
FastAutoGenerator.create( 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",
"root") "root")
.globalConfig(builder -> { .globalConfig(builder -> {
@ -74,7 +74,7 @@ public class TableToEntityConstructor {
} }
public static void main(String[] args) { public static void main(String[] args) {
AutoTable("chat", "", "", "", AutoTable("ad", "", "", "",
"acdr_chat_message"); "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 @TableLogic
private Boolean deleted; @TableField(value = "deleted", fill = FieldFill.INSERT)
private Boolean deleted = false;
@TableField(value = "create_time", fill = FieldFill.INSERT) @TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime; 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.PetExpertCertificationService;
import com.yskj.acdr.master.pet.service.PetInfoService; import com.yskj.acdr.master.pet.service.PetInfoService;
import com.yskj.acdr.master.pet.service.PetSpecialistCertificateService; import com.yskj.acdr.master.pet.service.PetSpecialistCertificateService;
import com.yskj.acdr.master.pet.status.PetExpertCertificationStatus;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.groups.Default; import jakarta.validation.groups.Default;
@ -166,5 +167,18 @@ public class PetController {
if (one == null || one.isEmpty()) return GlobalResponse.success(); if (one == null || one.isEmpty()) return GlobalResponse.success();
return GlobalResponse.success(one.getFirst()); 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") @GetMapping("/isPhoneBind")
public GlobalResponse<Boolean> isUsePhone() { public GlobalResponse<Boolean> isUsePhone() {
Users user = usersService.getById(StpUtil.getLoginIdAsLong()); Users user = usersService.getById(StpUtil.getLoginIdAsLong());

View File

@ -27,8 +27,8 @@ import java.time.LocalDateTime;
@ToString @ToString
@Accessors(chain = true) @Accessors(chain = true)
@TableName("acdr_users") @TableName("acdr_users")
@ApiModel(value = "UserBase对象", description = "") @ApiModel(value = "Users对象", description = "")
public class Users implements Serializable { public class Users extends ExtendEntity implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -81,14 +81,4 @@ public class Users implements Serializable {
@ApiModelProperty("是否拥有宠托师证书") @ApiModelProperty("是否拥有宠托师证书")
@TableField(value = "is_pet_nursery") @TableField(value = "is_pet_nursery")
private Boolean isPetNursery = false; 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() { public boolean isRealName() {
long userId = StpUtil.getLoginIdAsLong();
var query = new LambdaQueryWrapper<UserIdentityVerification>() var query = new LambdaQueryWrapper<UserIdentityVerification>()
.eq(UserIdentityVerification::getUserId, StpUtil.getLoginIdAsLong()); .eq(UserIdentityVerification::getUserId, userId);
var userIdEn = uidmapper.selectOne(query); var userIdEn = uidmapper.selectOne(query);
return userIdEn != null; return userIdEn != null;
} }