团队作业4——项目冲刺【Alpha阶段】第二次 Scrum Meeting

【Alpha阶段】第二次Scrum Meeting

这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 站立式会议+项目燃尽图+成员代码/文档签入记录+每人每日总结

Github仓库链接

团队队员

学号 姓名
3119005415 黄奕威
3219005447 连婉玲
3119005417 黄智权
3119005431 欧智昕
3219005448 刘淑婷
3119005410 冯波昌
3219005445 何婉莹

一、例会图片

lihui1.jpg

二、Burndown Chart

r1.png


三、代码/文档签入记录

z1.png

z2.png

z3.png

z4.png


四、项目进度

队员 昨日已完成任务 任务概述 今日待完成任务
黄奕威 时间安排表 · Issue #1总结 · Issue #2 完成项目时间安排表;完成主界面、用户界面、关卡界面的UI搭建 动画完善、UI完善、后台通讯尝试
连婉玲 总结 · Issue #6 完成了用户类的初始设计 完善用户类
黄智权 总结 · Issue #7 完成精灵类的总体设计 精灵升级策略、精灵技能策略实现;数据库精灵表、技能表的初始化
欧智昕 总结· Issue #5 完成了精灵类的基本设计 继续根据需求完善精灵类
刘淑婷 总结 · Issue #3 完成了后台框架的搭建 尝试与前端进行接口对接
冯波昌 总结 · Issue #4 完成道具类的初始设计 继续完善道具类
何婉莹 总结 · Issue #8 查看接口文档,初步规划接口 尝试与前端进行接口对接

五、最新模块代码

日志信息配置(点击查看)
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="30">
    <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->

    <!--变量配置-->
    <Properties>
        <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
        <!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<!--        <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS}{bright,green} [%thread] %-5level %logger{36}   %msg%n" />-->
        <property name="LOG_PATTERN" value="%d %highlight{%-5level}{ERROR=Bright RED, WARN=Bright Yellow, INFO=Bright Green, DEBUG=Bright Cyan, TRACE=Bright White} %style{[%t]}{bright,magenta} %style{%c.%M(%L)}{cyan}: %msg%n"/>
        <!-- 定义日志存储的路径 -->
        <property name="FILE_PATH" value="D:" />
        <property name="FILE_NAME" value="our_land" />
    </Properties>

    <appenders>

        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>

        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

    </appenders>

    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>

        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="Filelog"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>
UI(点击查看)
// 冒险页面
export default class Adventure extends Component {
  render () {
    return (
      <div className='adventure-container flex-start-stretch-col'>
        <div className='main-header flex-end-center'>
          <Link to='/user'>
            <Block
              img={require('../../assets/images/icons/avatar.jpg').default}
              size='small'
            />
          </Link>
        </div>
        <div className='adventure-content'>
          {adventure.map(item => (
            <Block
              className="adv-item"
              img={require('../../assets/images/icons/adventure.jpg').default}
              key={item.id}
              size='big'
              text={item.name}
            ></Block>
          ))}
        </div>
        <StoryPanel />
      </div>
    )
  }
}

// 主页模块
function Home () {
  let [isClick, setClick] = useState(false)
  if (isClick) {
    return (
      <div className='home-container flex-around-center-col'>
        <div className='title'>@信安1班——红橙黄绿青蓝紫队</div>
        <LoginPanel />
      </div>
    )
  } else {
    return (
      <div className='home-container flex-around-center-col'>
        <div className='title'>@信安1班——红橙黄绿青蓝紫队</div>
        <Button onClick={() => setClick(!isClick)}>
          <a>进入游戏</a>
        </Button>
      </div>
    )
  }
}

用户类初步实现(点击查看)
public class User {
    private int id;
    private int progress; //进度
    private String user_name;
    private String password;
    private String email;
    private ArrayList<Integer> spirits_bag; //精灵背包
    private ArrayList<Integer> props_bag; //道具背包
}
项目框架搭建(点击查看)
public class WebLogAspect {
    private static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

    @Pointcut("@annotation(ruangong.our_land.aspect.WebLog)")
    public void doWebLog(){
    }

    @Before("doWebLog()")
    public void before(JoinPoint joinPoint) throws ClassNotFoundException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //注解描述信息
        String description = getAspectLogDescription(joinPoint);
        logger.info("====================start=================");
        logger.info("URL         :{}",request.getRequestURL().toString());
        logger.info("Description             :{}",description);
        logger.info("HTTP Method    :{}",request.getMethod());
        logger.info("Class Method  :{}.{}",joinPoint.getSignature().getDeclaringTypeName(),joinPoint.getSignature().getName());
        logger.info("IP    :{}",request.getRemoteAddr());
        logger.info("Request Arags:    {}",new Gson().toJson(joinPoint.getArgs()));
    }

    @After("doWebLog()")
    public void after(){
        logger.info("===============end====================");
    }

    @Around("doWebLog()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = proceedingJoinPoint.proceed();
        return result;
    }

    public String getAspectLogDescription(JoinPoint joinPoint) throws ClassNotFoundException {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Class<?> target = Class.forName(targetName);
        Method[] methods = target.getMethods();
        StringBuilder builder = new StringBuilder();
        for (Method method:methods){
            if (method.getName().equals(methodName)){
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length==args.length){
                    builder.append(method.getAnnotation(WebLog.class).message());
                    break;
                }
            }
        }
        return builder.toString();
    }

    @AfterReturning(pointcut = "doWebLog()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        logger.info("Response Args:      {}",new Gson().toJson(result));
    }

    @AfterThrowing(pointcut = "doWebLog()",throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint,Exception exception){
        logger.info("Exception e:      {}",exception.getMessage());
    }
}
精灵类实现(点击查看)
package ruangong.our_land.model.spirit;

import lombok.Data;
import lombok.Getter;
import org.springframework.lang.NonNull;
import ruangong.our_land.model.helper.ObjectHelper;
import ruangong.our_land.model.spirit.monster.Monster;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.util.HashMap;
import java.util.Map;

/**
 * 精灵类,包括boss、用户初始和野怪
 *
 * @author HuangZhiquan
 * @author Wizardk
 * @Description 精灵类
 * @date Created in 2021-11-02 22:57
 * @Modified By
 */
@Data
public abstract class Spirit {

    /**
     * 精灵的最高等级
     */
    @Max(value = 100, message = "等级最高为100")
    public static final int MAX_LEVEL = 100;

    /**
     * 精灵名
     */
    @NonNull
    @Getter
    private final String name;

    /**
     * 精灵的id
     */
    @NonNull
    @Getter
    private final String id;

    /**
     * 精灵是否稀有(是:1,否:0)
     * 精灵稀有与否,关系到精灵捕捉时的成功率
     */
    @NonNull
    @Getter
    private int isRare;

    /**
     * 等级
     */
    @Getter
    @Min(value = 1, message = "等级最低为1")
    private int level;
    /**
     * 血量
     */
    @Getter
    @Min(value = 0)
    private int blood;
    /**
     * 攻击力
     */
    @Getter
    @Min(value = 0)
    private static int attack;
    /**
     * 防御力
     */
    @Getter
    @Min(value = 0)
    private static int defence;
    /**
     * 速度
     */
    @Getter
    @Min(value = 0)
    private static int speed;

    /**
     * 精灵类型(初始、野怪或boss)
     */
    private String type;

    /**
     * 精灵属性(水、火、草)
     */
    private String nature;

    /**
     * 存放精灵的4个技能
     */
    @NonNull
    private Map<String, Skill> skillMap;

    //构造方法
    public Spirit(String name, String id, int level, int blood, int attack,
                  int defense, int speed,String type,String nature,int isRare) {
        this.name = name;
        this.id = id;
        this.level = level;
        this.blood = blood;
        this.attack = attack;
        this.defence = defense;
        this.speed = speed;
        this.type = type;
        this.nature = nature;
        this.isRare = isRare;
        setSkills();
    }

    private void setSkills() {
        if (skillMap == null) {
            Skill[] skills = ObjectHelper.requireNonNull(initSkills());
            this.skillMap = new HashMap<>(4);
            for (Skill skill : skills) {
                skillMap.put(skill.name, skill);
            }
        }
    }

    /**
     * 初始化技能
     * @return 返回技能列表
     */
    protected abstract Skill[] initSkills();

    /**
     * 根据技能名获取相应技能
     *
     * @param skillName 技能名
     * @return 返回对应的技能类对象
     */
    public Skill getSkills(String skillName) {
        String skill = ObjectHelper.requireNonNull(skillName, "skillName");
        if (skillMap == null) {
            return null;
        }
        if (!skillMap.containsKey(skill)) {
            throw new IllegalArgumentException("The skill \" " + skill + " \" not found! Please recheck!");
        }
        return skillMap.get(skill);
    }


    /**
     * 升级,使level+1
     */
    protected void levelUp() {
        if (this instanceof Monster && this.level < MAX_LEVEL) {
            this.level++;
        }
    }

    public void setBlood(int blood) {
        this.blood = ObjectHelper.verifyNonZeroPositive(blood, "blood");
    }

    public void setAttack(int attack) {
        this.attack = ObjectHelper.verifyNonZeroPositive(attack, "attack");
    }

    public void setDefence(int defense) {
        this.defence = ObjectHelper.verifyNonZeroPositive(defense, "defense");
    }

    public void setSpeed(int speed) {
        this.speed = ObjectHelper.verifyNonZeroPositive(speed, "speed");
    }

    /**
     * 技能类
     */
    @Data
    public static class Skill {
        //技能基本属性
        /**
         * 技能名
         */
        @NonNull
        @Getter
        String name;
        /**
         * 技能描述
         */
        @NonNull
        @Getter
        String description;
        /**
         * 技能使用次数
         */
        @Getter
        int times = 0;

        /**
         * 技能类型(伤害型或提升型)
         * 伤害型:基础伤害值
         * 提升型:提升精灵属性(攻击、防御、速度)
         */
        @NonNull
        @Getter
        String type;

        /**
         * 技能伤害(若技能为伤害型),用于精灵对战
         */
        @Getter
        int hurt;

        //提升型技能的构造方法
        public Skill(String name, String descrp,String type) {
            this.name = ObjectHelper.requireNonNull(name, "name");
            this.description = ObjectHelper.requireNonNull(descrp, "descrp");
            this.type = ObjectHelper.requireNonNull(type, "type");
        }

        //伤害型技能的构造方法
        public Skill(String name, String descrp,String type,int hurt) {
            this.name = ObjectHelper.requireNonNull(name, "name");
            this.description = ObjectHelper.requireNonNull(descrp, "descrp");
            this.type = ObjectHelper.requireNonNull(type, "type");
            this.hurt = hurt;
        }

        /**
         * 技能效果:
         * 伤害型:更新技能伤害值(用于对战)
         * 提升型:更新精灵属性(仅限于对战)
         */
        public void skillEffect(){
            if(this.type.equals("伤害型")){
                //当前伤害值 = 伤害值 * 攻击力
                this.hurt = this.hurt * attack;
            }else if(this.type.equals("提升型")){
                //通过判断技能描述,来判断是提升攻击力、防御力还是速度
                //开始为简单起见,假设属性提升按+1的方式提升
                if(this.description.contains("攻击力")){
                    attack = attack + 1;
                }else if(this.description.contains("防御力")){
                    defence = defence + 1;
                }else if(this.description.contains("速度")){
                    speed = speed + 1;
                }
            }
        }

        public void setTimes(int times) {
            this.times = ObjectHelper.verifyNonZeroPositive(times, "times");
        }
    }
}

六、遇到的困难

  1. UI逻辑较为复杂,精灵对战动画效果实现不佳,需进一步完善。

  2. 精灵类中关于精灵升级策略、属性提升策略、精灵类继承问题有待解决。

  3. 接口逻辑还需进一步理清。

  4. 以矩阵形式随机生成野怪地图较难。


七、每人每日总结

黄奕威:UI逻辑较为复杂,精灵对战动画效果实现不佳,需进一步完善。

连婉玲:大家首次合作,配合得尚且不佳,后续需要更多的沟通与交流,以提升之后的任务完成效率。

黄智权:精灵类中关于精灵升级策略、属性提升策略、精灵类继承问题有待解决。

欧智昕:时间管理尤为重要,根据各成员的分工与实际能力,客观分析预计完成时间,合理评估各自的执行力与任务完成效率,以便对各模块进行灵活的人员调整。

刘淑婷:敏捷开发需要定期集结各位成员进行项目进程的商讨,各抒己见、集思广益,充分有效的沟通十分重要,各成员的分工需明确且清晰,讨论确定当日计划,并动手付诸实践。

冯波昌:只要思想不滑坡,办法总比困难多。要沉得住气,静得下心去思考问题,一个人的半途而废会严重影响整个团队的进程。

何婉莹:在代码实现的过程中,难免会遇到自己难以解决的实质性难题,寻求队友的帮助不失为一种好方法。相互交换思考过程,有助于彼此逻辑能力的提升。

posted @ 2021-11-23 20:34  hzq--  阅读(54)  评论(0编辑  收藏  举报